diff --git a/backportpackage b/backportpackage index 13a7a5e..c86586a 100755 --- a/backportpackage +++ b/backportpackage @@ -34,18 +34,20 @@ except ImportError: from httplib2 import Http, HttpLib2Error from distro_info import DebianDistroInfo, UbuntuDistroInfo -from ubuntutools.archive import (DebianSourcePackage, - UbuntuSourcePackage, DownloadError) +from ubuntutools.archive import DebianSourcePackage, UbuntuSourcePackage, DownloadError from ubuntutools.config import UDTConfig, ubu_email from ubuntutools.builder import get_builder -from ubuntutools.lp.lpapicache import (Launchpad, Distribution, - SeriesNotFoundException, - PackageNotFoundException) -from ubuntutools.misc import (system_distribution, vendor_to_distroinfo, - codename_to_distribution) +from ubuntutools.lp.lpapicache import ( + Launchpad, + Distribution, + SeriesNotFoundException, + PackageNotFoundException, +) +from ubuntutools.misc import system_distribution, vendor_to_distroinfo, codename_to_distribution from ubuntutools.question import YesNoQuestion from ubuntutools import getLogger + Logger = getLogger() @@ -55,130 +57,152 @@ def error(msg): def check_call(cmd, *args, **kwargs): - Logger.debug(' '.join(cmd)) + Logger.debug(" ".join(cmd)) ret = subprocess.call(cmd, *args, **kwargs) if ret != 0: - error('%s returned %d.' % (cmd[0], ret)) + error("%s returned %d." % (cmd[0], ret)) def parse(args): - usage = 'Usage: %prog [options] ' + usage = "Usage: %prog [options] " parser = optparse.OptionParser(usage) - parser.add_option('-d', '--destination', - metavar='DEST', - dest='dest_releases', - default=[], - action='append', - help='Backport to DEST release ' - '(default: current release)') - parser.add_option('-s', '--source', - metavar='SOURCE', - dest='source_release', - help='Backport from SOURCE release ' - '(default: devel release)') - parser.add_option('-S', '--suffix', - metavar='SUFFIX', - help='Suffix to append to version number ' - '(default: ~ppa1 when uploading to a PPA)') - parser.add_option('-e', '--message', - metavar='MESSAGE', - default="No-change", - help='Changelog message to use instead of "No-change" ' - '(default: No-change backport to DEST.)') - parser.add_option('-b', '--build', - default=False, - action='store_true', - help='Build the package before uploading ' - '(default: %default)') - parser.add_option('-B', '--builder', - metavar='BUILDER', - help='Specify the package builder (default: pbuilder)') - parser.add_option('-U', '--update', - default=False, - action='store_true', - help='Update the build environment before ' - 'attempting to build') - parser.add_option('-u', '--upload', - metavar='UPLOAD', - help='Specify an upload destination') - parser.add_option("-k", "--key", - dest='keyid', - help="Specify the key ID to be used for signing.") - parser.add_option('--dont-sign', - dest='keyid', action='store_false', - help='Do not sign the upload.') - parser.add_option('-y', '--yes', - dest='prompt', - default=True, - action='store_false', - help='Do not prompt before uploading to a PPA') - parser.add_option('-v', '--version', - metavar='VERSION', - help='Package version to backport (or verify)') - parser.add_option('-w', '--workdir', - metavar='WORKDIR', - help='Specify a working directory ' - '(default: temporary dir)') - parser.add_option('-r', '--release-pocket', - default=False, - action='store_true', - help='Target the release pocket in the .changes file. ' - 'Necessary (and default) for uploads to PPAs') - parser.add_option('-c', '--close', - metavar='BUG', - help='Bug to close in the changelog entry.') - parser.add_option('-m', '--mirror', - metavar='URL', - help='Preferred mirror (default: Launchpad)') - parser.add_option('-l', '--lpinstance', - metavar='INSTANCE', - help='Launchpad instance to connect to ' - '(default: production)') - parser.add_option('--no-conf', - default=False, - action='store_true', - help="Don't read config files or environment variables") + parser.add_option( + "-d", + "--destination", + metavar="DEST", + dest="dest_releases", + default=[], + action="append", + help="Backport to DEST release (default: current release)", + ) + parser.add_option( + "-s", + "--source", + metavar="SOURCE", + dest="source_release", + help="Backport from SOURCE release (default: devel release)", + ) + parser.add_option( + "-S", + "--suffix", + metavar="SUFFIX", + help="Suffix to append to version number (default: ~ppa1 when uploading to a PPA)", + ) + parser.add_option( + "-e", + "--message", + metavar="MESSAGE", + default="No-change", + help='Changelog message to use instead of "No-change" ' + "(default: No-change backport to DEST.)", + ) + parser.add_option( + "-b", + "--build", + default=False, + action="store_true", + help="Build the package before uploading (default: %default)", + ) + parser.add_option( + "-B", + "--builder", + metavar="BUILDER", + help="Specify the package builder (default: pbuilder)", + ) + parser.add_option( + "-U", + "--update", + default=False, + action="store_true", + help="Update the build environment before attempting to build", + ) + parser.add_option("-u", "--upload", metavar="UPLOAD", help="Specify an upload destination") + parser.add_option( + "-k", "--key", dest="keyid", help="Specify the key ID to be used for signing." + ) + parser.add_option( + "--dont-sign", dest="keyid", action="store_false", help="Do not sign the upload." + ) + parser.add_option( + "-y", + "--yes", + dest="prompt", + default=True, + action="store_false", + help="Do not prompt before uploading to a PPA", + ) + parser.add_option( + "-v", "--version", metavar="VERSION", help="Package version to backport (or verify)" + ) + parser.add_option( + "-w", + "--workdir", + metavar="WORKDIR", + help="Specify a working directory (default: temporary dir)", + ) + parser.add_option( + "-r", + "--release-pocket", + default=False, + action="store_true", + help="Target the release pocket in the .changes file. " + "Necessary (and default) for uploads to PPAs", + ) + parser.add_option("-c", "--close", metavar="BUG", help="Bug to close in the changelog entry.") + parser.add_option( + "-m", "--mirror", metavar="URL", help="Preferred mirror (default: Launchpad)" + ) + parser.add_option( + "-l", + "--lpinstance", + metavar="INSTANCE", + help="Launchpad instance to connect to (default: production)", + ) + parser.add_option( + "--no-conf", + default=False, + action="store_true", + help="Don't read config files or environment variables", + ) opts, args = parser.parse_args(args) if len(args) != 1: - parser.error('You must specify a single source package or a .dsc ' - 'URL/path.') + parser.error("You must specify a single source package or a .dsc URL/path.") config = UDTConfig(opts.no_conf) if opts.builder is None: - opts.builder = config.get_value('BUILDER') + opts.builder = config.get_value("BUILDER") if not opts.update: - opts.update = config.get_value('UPDATE_BUILDER', boolean=True) + opts.update = config.get_value("UPDATE_BUILDER", boolean=True) if opts.workdir is None: - opts.workdir = config.get_value('WORKDIR') + opts.workdir = config.get_value("WORKDIR") if opts.lpinstance is None: - opts.lpinstance = config.get_value('LPINSTANCE') + opts.lpinstance = config.get_value("LPINSTANCE") if opts.upload is None: - opts.upload = config.get_value('UPLOAD') + opts.upload = config.get_value("UPLOAD") if opts.keyid is None: - opts.keyid = config.get_value('KEYID') + opts.keyid = config.get_value("KEYID") if not opts.upload and not opts.workdir: - parser.error('Please specify either a working dir or an upload target!') - if opts.upload and opts.upload.startswith('ppa:'): + parser.error("Please specify either a working dir or an upload target!") + if opts.upload and opts.upload.startswith("ppa:"): opts.release_pocket = True return opts, args, config -def find_release_package(mirror, workdir, package, version, source_release, - config): +def find_release_package(mirror, workdir, package, version, source_release, config): srcpkg = None if source_release: distribution = codename_to_distribution(source_release) if not distribution: - error('Unknown release codename %s' % source_release) + error("Unknown release codename %s" % source_release) info = vendor_to_distroinfo(distribution)() source_release = info.codename(source_release, default=source_release) else: distribution = system_distribution() mirrors = [mirror] if mirror else [] - mirrors.append(config.get_value('%s_MIRROR' % distribution.upper())) + mirrors.append(config.get_value("%s_MIRROR" % distribution.upper())) if not version: archive = Distribution(distribution.lower()).getArchive() @@ -188,39 +212,35 @@ def find_release_package(mirror, workdir, package, version, source_release, error(str(e)) version = spph.getVersion() - if distribution == 'Debian': - srcpkg = DebianSourcePackage(package, - version, - workdir=workdir, - mirrors=mirrors) - elif distribution == 'Ubuntu': - srcpkg = UbuntuSourcePackage(package, - version, - workdir=workdir, - mirrors=mirrors) + if distribution == "Debian": + srcpkg = DebianSourcePackage(package, version, workdir=workdir, mirrors=mirrors) + elif distribution == "Ubuntu": + srcpkg = UbuntuSourcePackage(package, version, workdir=workdir, mirrors=mirrors) return srcpkg def find_package(mirror, workdir, package, version, source_release, config): "Returns the SourcePackage" - if package.endswith('.dsc'): + if package.endswith(".dsc"): # Here we are using UbuntuSourcePackage just because we don't have any # "general" class that is safely instantiable (as SourcePackage is an # abstract class). None of the distribution-specific details within # UbuntuSourcePackage is relevant for this use case. - return UbuntuSourcePackage(version=version, dscfile=package, - workdir=workdir, mirrors=(mirror,)) + return UbuntuSourcePackage( + version=version, dscfile=package, workdir=workdir, mirrors=(mirror,) + ) if not source_release and not version: info = vendor_to_distroinfo(system_distribution()) source_release = info().devel() - srcpkg = find_release_package(mirror, workdir, package, version, - source_release, config) + srcpkg = find_release_package(mirror, workdir, package, version, source_release, config) if version and srcpkg.version != version: - error('Requested backport of version %s but version of %s in %s is %s' - % (version, package, source_release, srcpkg.version)) + error( + "Requested backport of version %s but version of %s in %s is %s" + % (version, package, source_release, srcpkg.version) + ) return srcpkg @@ -228,30 +248,27 @@ def find_package(mirror, workdir, package, version, source_release, config): def get_backport_version(version, suffix, upload, release): distribution = codename_to_distribution(release) if not distribution: - error('Unknown release codename %s' % release) - if distribution == 'Debian': + error("Unknown release codename %s" % release) + if distribution == "Debian": debian_distro_info = DebianDistroInfo() debian_codenames = debian_distro_info.supported() if release in debian_codenames: release_version = debian_distro_info.version(release) if not release_version: error(f"Can't find the release version for {release}") - backport_version = "{}~bpo{}+1".format( - version, release_version - ) + backport_version = "{}~bpo{}+1".format(version, release_version) else: error(f"{release} is not a supported release ({debian_codenames})") - elif distribution == 'Ubuntu': - series = Distribution(distribution.lower()).\ - getSeries(name_or_version=release) + elif distribution == "Ubuntu": + series = Distribution(distribution.lower()).getSeries(name_or_version=release) - backport_version = version + ('~bpo%s.1' % (series.version)) + backport_version = version + ("~bpo%s.1" % (series.version)) else: - error('Unknown distribution «%s» for release «%s»' % (distribution, release)) + error("Unknown distribution «%s» for release «%s»" % (distribution, release)) if suffix is not None: backport_version += suffix - elif upload and upload.startswith('ppa:'): - backport_version += '~ppa1' + elif upload and upload.startswith("ppa:"): + backport_version += "~ppa1" return backport_version @@ -259,10 +276,9 @@ def get_old_version(source, release): try: distribution = codename_to_distribution(release) archive = Distribution(distribution.lower()).getArchive() - pkg = archive.getSourcePackage(source, - release, - ('Release', 'Security', 'Updates', - 'Proposed', 'Backports')) + pkg = archive.getSourcePackage( + source, release, ("Release", "Security", "Updates", "Proposed", "Backports") + ) return pkg.getVersion() except (SeriesNotFoundException, PackageNotFoundException): pass @@ -272,7 +288,7 @@ def get_backport_dist(release, release_pocket): if release_pocket: return release else: - return '%s-backports' % release + return "%s-backports" % release def do_build(workdir, dsc, release, builder, update): @@ -286,41 +302,43 @@ def do_build(workdir, dsc, release, builder, update): # builder.build is going to chdir to buildresult: workdir = os.path.realpath(workdir) - return builder.build(os.path.join(workdir, dsc), - release, - os.path.join(workdir, "buildresult")) + return builder.build(os.path.join(workdir, dsc), release, os.path.join(workdir, "buildresult")) def do_upload(workdir, package, bp_version, changes, upload, prompt): - print('Please check %s %s in file://%s carefully!' % (package, bp_version, workdir)) - if prompt or upload == 'ubuntu': - question = 'Do you want to upload the package to %s' % upload + print("Please check %s %s in file://%s carefully!" % (package, bp_version, workdir)) + if prompt or upload == "ubuntu": + question = "Do you want to upload the package to %s" % upload answer = YesNoQuestion().ask(question, "yes") if answer == "no": return - check_call(['dput', upload, changes], cwd=workdir) + check_call(["dput", upload, changes], cwd=workdir) def orig_needed(upload, workdir, pkg): - '''Avoid a -sa if possible''' - if not upload or not upload.startswith('ppa:'): + """Avoid a -sa if possible""" + if not upload or not upload.startswith("ppa:"): return True - ppa = upload.split(':', 1)[1] - user, ppa = ppa.split('/', 1) + ppa = upload.split(":", 1)[1] + user, ppa = ppa.split("/", 1) version = pkg.version.upstream_version h = Http() - for filename in glob.glob(os.path.join(workdir, '%s_%s.orig*' % (pkg.source, version))): - url = ('https://launchpad.net/~%s/+archive/%s/+sourcefiles/%s/%s/%s' - % (quote(user), quote(ppa), quote(pkg.source), - quote(pkg.version.full_version), - quote(os.path.basename(filename)))) + for filename in glob.glob(os.path.join(workdir, "%s_%s.orig*" % (pkg.source, version))): + url = "https://launchpad.net/~%s/+archive/%s/+sourcefiles/%s/%s/%s" % ( + quote(user), + quote(ppa), + quote(pkg.source), + quote(pkg.version.full_version), + quote(os.path.basename(filename)), + ) try: - headers, body = h.request(url, 'HEAD') - if (headers.status != 200 or - not headers['content-location'].startswith('https://launchpadlibrarian.net')): + headers, body = h.request(url, "HEAD") + if headers.status != 200 or not headers["content-location"].startswith( + "https://launchpadlibrarian.net" + ): return True except HttpLib2Error as e: Logger.debug(e) @@ -328,61 +346,79 @@ def orig_needed(upload, workdir, pkg): return False -def do_backport(workdir, pkg, suffix, message, close, release, release_pocket, - build, builder, update, upload, keyid, prompt): - dirname = '%s-%s' % (pkg.source, release) +def do_backport( + workdir, + pkg, + suffix, + message, + close, + release, + release_pocket, + build, + builder, + update, + upload, + keyid, + prompt, +): + dirname = "%s-%s" % (pkg.source, release) srcdir = os.path.join(workdir, dirname) if os.path.exists(srcdir): - question = 'Working directory %s already exists. Delete it?' % srcdir - if YesNoQuestion().ask(question, 'no') == 'no': + question = "Working directory %s already exists. Delete it?" % srcdir + if YesNoQuestion().ask(question, "no") == "no": sys.exit(1) shutil.rmtree(srcdir) pkg.unpack(dirname) - bp_version = get_backport_version(pkg.version.full_version, suffix, - upload, release) + bp_version = get_backport_version(pkg.version.full_version, suffix, upload, release) old_version = get_old_version(pkg.source, release) bp_dist = get_backport_dist(release, release_pocket) - changelog = '%s backport to %s.' % (message, release,) + changelog = "%s backport to %s." % (message, release) if close: - changelog += ' (LP: #%s)' % (close,) - check_call(['dch', - '--force-bad-version', - '--force-distribution', - '--preserve', - '--newversion', bp_version, - '--distribution', bp_dist, - changelog], - cwd=srcdir) + changelog += " (LP: #%s)" % (close,) + check_call( + [ + "dch", + "--force-bad-version", + "--force-distribution", + "--preserve", + "--newversion", + bp_version, + "--distribution", + bp_dist, + changelog, + ], + cwd=srcdir, + ) - cmd = ['debuild', '--no-lintian', '-S', '-nc', '-uc', '-us'] + cmd = ["debuild", "--no-lintian", "-S", "-nc", "-uc", "-us"] if orig_needed(upload, workdir, pkg): - cmd.append('-sa') + cmd.append("-sa") else: - cmd.append('-sd') + cmd.append("-sd") if old_version: - cmd.append('-v%s' % old_version) + cmd.append("-v%s" % old_version) env = os.environ.copy() # An ubuntu.com e-mail address would make dpkg-buildpackage fail if there # wasn't an Ubuntu maintainer for an ubuntu-versioned package. LP: #1007042 - env.pop('DEBEMAIL', None) + env.pop("DEBEMAIL", None) check_call(cmd, cwd=srcdir, env=env) - fn_base = pkg.source + '_' + bp_version.split(':', 1)[-1] - changes = fn_base + '_source.changes' + fn_base = pkg.source + "_" + bp_version.split(":", 1)[-1] + changes = fn_base + "_source.changes" if build: - if 0 != do_build(workdir, fn_base + '.dsc', release, builder, update): + if 0 != do_build(workdir, fn_base + ".dsc", release, builder, update): sys.exit(1) # None: sign with the default signature. False: don't sign if keyid is not False: - cmd = ['debsign'] + cmd = ["debsign"] if keyid: - cmd.append('-k' + keyid) + cmd.append("-k" + keyid) cmd.append(changes) check_call(cmd, cwd=workdir) if upload: @@ -402,13 +438,13 @@ def main(args): if lsb_release: distinfo = lsb_release.get_distro_information() try: - current_distro = distinfo['ID'] + current_distro = distinfo["ID"] except KeyError: - error('No destination release specified and unable to guess yours.') + error("No destination release specified and unable to guess yours.") else: - err, current_distro = subprocess.getstatusoutput('lsb_release --id --short') + err, current_distro = subprocess.getstatusoutput("lsb_release --id --short") if err: - error('Could not run lsb_release to retrieve distribution') + error("Could not run lsb_release to retrieve distribution") if current_distro == "Ubuntu": opts.dest_releases = [UbuntuDistroInfo().lts()] @@ -420,34 +456,33 @@ def main(args): if opts.workdir: workdir = os.path.expanduser(opts.workdir) else: - workdir = tempfile.mkdtemp(prefix='backportpackage-') + workdir = tempfile.mkdtemp(prefix="backportpackage-") if not os.path.exists(workdir): os.makedirs(workdir) try: - pkg = find_package(opts.mirror, - workdir, - package_or_dsc, - opts.version, - opts.source_release, - config) + pkg = find_package( + opts.mirror, workdir, package_or_dsc, opts.version, opts.source_release, config + ) pkg.pull() for release in opts.dest_releases: - do_backport(workdir, - pkg, - opts.suffix, - opts.message, - opts.close, - release, - opts.release_pocket, - opts.build, - opts.builder, - opts.update, - opts.upload, - opts.keyid, - opts.prompt) + do_backport( + workdir, + pkg, + opts.suffix, + opts.message, + opts.close, + release, + opts.release_pocket, + opts.build, + opts.builder, + opts.update, + opts.upload, + opts.keyid, + opts.prompt, + ) except DownloadError as e: error(str(e)) finally: @@ -455,5 +490,5 @@ def main(args): shutil.rmtree(workdir) -if __name__ == '__main__': +if __name__ == "__main__": sys.exit(main(sys.argv)) diff --git a/bitesize b/bitesize index 04d1eef..bcb9cb0 100755 --- a/bitesize +++ b/bitesize @@ -30,6 +30,7 @@ from launchpadlib.errors import HTTPError from ubuntutools.config import UDTConfig from ubuntutools import getLogger + Logger = getLogger() @@ -46,21 +47,28 @@ def save_entry(entry): def tag_bug(bug): - bug.tags = bug.tags + ['bitesize'] # LP: #254901 workaround + bug.tags = bug.tags + ["bitesize"] # LP: #254901 workaround save_entry(bug) def main(): usage = "Usage: %prog " opt_parser = OptionParser(usage) - opt_parser.add_option("-l", "--lpinstance", metavar="INSTANCE", - help="Launchpad instance to connect to " - "(default: production)", - dest="lpinstance", default=None) - opt_parser.add_option("--no-conf", - help="Don't read config files or " - "environment variables.", - dest="no_conf", default=False, action="store_true") + opt_parser.add_option( + "-l", + "--lpinstance", + metavar="INSTANCE", + help="Launchpad instance to connect to (default: production)", + dest="lpinstance", + default=None, + ) + opt_parser.add_option( + "--no-conf", + help="Don't read config files or environment variables.", + dest="no_conf", + default=False, + action="store_true", + ) (options, args) = opt_parser.parse_args() config = UDTConfig(options.no_conf) if options.lpinstance is None: @@ -77,19 +85,22 @@ def main(): bug = launchpad.bugs[args[0]] except HTTPError as error: if error.response.status == 401: - error_out("Don't have enough permissions to access bug %s. %s" % - (args[0], error.content)) + error_out( + "Don't have enough permissions to access bug %s. %s" % (args[0], error.content) + ) else: raise - if 'bitesize' in bug.tags: + if "bitesize" in bug.tags: error_out("Bug is already marked as 'bitesize'.") - bug.newMessage(content="I'm marking this bug as 'bitesize' as it looks " - "like an issue that is easy to fix and suitable " - "for newcomers in Ubuntu development. If you need " - "any help with fixing it, talk to me about it.") + bug.newMessage( + content="I'm marking this bug as 'bitesize' as it looks " + "like an issue that is easy to fix and suitable " + "for newcomers in Ubuntu development. If you need " + "any help with fixing it, talk to me about it." + ) bug.subscribe(person=launchpad.me) tag_bug(launchpad.bugs[bug.id]) # fresh bug object, LP: #336866 workaround -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/check-mir b/check-mir index b7ebe83..92f770d 100755 --- a/check-mir +++ b/check-mir @@ -29,57 +29,58 @@ import apt def check_support(apt_cache, pkgname, alt=False): - '''Check if pkgname is in main or restricted. + """Check if pkgname is in main or restricted. This prints messages if a package is not in main/restricted, or only partially (i. e. source in main, but binary in universe). - ''' + """ if alt: - prefix = ' ... alternative ' + pkgname + prefix = " ... alternative " + pkgname else: - prefix = ' * ' + pkgname + prefix = " * " + pkgname try: pkg = apt_cache[pkgname] except KeyError: - print(prefix, 'does not exist (pure virtual?)', file=sys.stderr) + print(prefix, "does not exist (pure virtual?)", file=sys.stderr) return False section = pkg.candidate.section - if section.startswith('universe') or section.startswith('multiverse'): + if section.startswith("universe") or section.startswith("multiverse"): # check if the source package is in main and thus will only need binary # promotion source_records = apt.apt_pkg.SourceRecords() if not source_records.lookup(pkg.candidate.source_name): - print('ERROR: Cannot lookup source package for', pkg.name, - file=sys.stderr) - print(prefix, 'package is in', section.split('/')[0]) + print("ERROR: Cannot lookup source package for", pkg.name, file=sys.stderr) + print(prefix, "package is in", section.split("/")[0]) return False src = apt.apt_pkg.TagSection(source_records.record) - if (src['Section'].startswith('universe') or - src['Section'].startswith('multiverse')): - print(prefix, 'binary and source package is in', - section.split('/')[0]) + if src["Section"].startswith("universe") or src["Section"].startswith("multiverse"): + print(prefix, "binary and source package is in", section.split("/")[0]) return False else: - print(prefix, 'is in', section.split('/')[0] + ', but its source', - pkg.candidate.source_name, - 'is already in main; file an ubuntu-archive bug for ' - 'promoting the current preferred alternative') + print( + prefix, + "is in", + section.split("/")[0] + ", but its source", + pkg.candidate.source_name, + "is already in main; file an ubuntu-archive bug for " + "promoting the current preferred alternative", + ) return True if alt: - print(prefix, 'is already in main; consider preferring it') + print(prefix, "is already in main; consider preferring it") return True def check_build_dependencies(apt_cache, control): - print('Checking support status of build dependencies...') + print("Checking support status of build dependencies...") any_unsupported = False - for field in ('Build-Depends', 'Build-Depends-Indep'): + for field in ("Build-Depends", "Build-Depends-Indep"): if field not in control.section: continue for or_group in apt.apt_pkg.parse_src_depends(control.section[field]): @@ -98,20 +99,19 @@ def check_build_dependencies(apt_cache, control): def check_binary_dependencies(apt_cache, control): any_unsupported = False - print('\nChecking support status of binary dependencies...') + print("\nChecking support status of binary dependencies...") while True: try: next(control) except StopIteration: break - for field in ('Depends', 'Pre-Depends', 'Recommends'): + for field in ("Depends", "Pre-Depends", "Recommends"): if field not in control.section: continue - for or_group in apt.apt_pkg.parse_src_depends( - control.section[field]): + for or_group in apt.apt_pkg.parse_src_depends(control.section[field]): pkgname = or_group[0][0] - if pkgname.startswith('$'): + if pkgname.startswith("$"): continue if not check_support(apt_cache, pkgname): # check non-preferred alternatives @@ -125,32 +125,38 @@ def check_binary_dependencies(apt_cache, control): def main(): - description = "Check if any of a package's build or binary " + \ - "dependencies are in universe or multiverse. " + \ - "Run this inside an unpacked source package" + description = ( + "Check if any of a package's build or binary " + + "dependencies are in universe or multiverse. " + + "Run this inside an unpacked source package" + ) parser = optparse.OptionParser(description=description) parser.parse_args() apt_cache = apt.Cache() - if not os.path.exists('debian/control'): - print('debian/control not found. You need to run this tool in a ' - 'source package directory', file=sys.stderr) + if not os.path.exists("debian/control"): + print( + "debian/control not found. You need to run this tool in a source package directory", + file=sys.stderr, + ) sys.exit(1) # get build dependencies from debian/control - control = apt.apt_pkg.TagFile(open('debian/control')) + control = apt.apt_pkg.TagFile(open("debian/control")) next(control) unsupported_build_deps = check_build_dependencies(apt_cache, control) unsupported_binary_deps = check_binary_dependencies(apt_cache, control) if unsupported_build_deps or unsupported_binary_deps: - print('\nPlease check https://wiki.ubuntu.com/MainInclusionProcess if ' - 'this source package needs to get into in main/restricted, or ' - 'reconsider if the package really needs above dependencies.') + print( + "\nPlease check https://wiki.ubuntu.com/MainInclusionProcess if " + "this source package needs to get into in main/restricted, or " + "reconsider if the package really needs above dependencies." + ) else: - print('All dependencies are supported in main or restricted.') + print("All dependencies are supported in main or restricted.") -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/enforced-editing-wrapper b/enforced-editing-wrapper index 36a1b55..cce1876 100755 --- a/enforced-editing-wrapper +++ b/enforced-editing-wrapper @@ -30,33 +30,33 @@ from ubuntutools.question import EditFile def main(): - parser = optparse.OptionParser('%prog [options] filename') + parser = optparse.OptionParser("%prog [options] filename") options, args = parser.parse_args() if len(args) != 1: - parser.error('A filename must be specified') + parser.error("A filename must be specified") body = args[0] if not os.path.isfile(body): - parser.error('File %s does not exist' % body) + parser.error("File %s does not exist" % body) - if 'UDT_EDIT_WRAPPER_EDITOR' in os.environ: - os.environ['EDITOR'] = os.environ['UDT_EDIT_WRAPPER_EDITOR'] + if "UDT_EDIT_WRAPPER_EDITOR" in os.environ: + os.environ["EDITOR"] = os.environ["UDT_EDIT_WRAPPER_EDITOR"] else: - del os.environ['EDITOR'] + del os.environ["EDITOR"] - if 'UDT_EDIT_WRAPPER_VISUAL' in os.environ: - os.environ['VISUAL'] = os.environ['UDT_EDIT_WRAPPER_VISUAL'] + if "UDT_EDIT_WRAPPER_VISUAL" in os.environ: + os.environ["VISUAL"] = os.environ["UDT_EDIT_WRAPPER_VISUAL"] else: - del os.environ['VISUAL'] + del os.environ["VISUAL"] placeholders = [] - if 'UDT_EDIT_WRAPPER_TEMPLATE_RE' in os.environ: - placeholders.append(re.compile( - os.environ['UDT_EDIT_WRAPPER_TEMPLATE_RE'])) + if "UDT_EDIT_WRAPPER_TEMPLATE_RE" in os.environ: + placeholders.append(re.compile(os.environ["UDT_EDIT_WRAPPER_TEMPLATE_RE"])) - description = os.environ.get('UDT_EDIT_WRAPPER_FILE_DESCRIPTION', 'file') + description = os.environ.get("UDT_EDIT_WRAPPER_FILE_DESCRIPTION", "file") EditFile(body, description, placeholders).edit() -if __name__ == '__main__': + +if __name__ == "__main__": main() diff --git a/grep-merges b/grep-merges index 6a1b895..c50293f 100755 --- a/grep-merges +++ b/grep-merges @@ -28,17 +28,19 @@ from httplib2 import Http, HttpLib2Error import ubuntutools.misc from ubuntutools import getLogger + Logger = getLogger() def main(): parser = optparse.OptionParser( - usage='%prog [options] [string]', - description='List pending merges from Debian matching string') + usage="%prog [options] [string]", + description="List pending merges from Debian matching string", + ) args = parser.parse_args()[1] if len(args) > 1: - parser.error('Too many arguments') + parser.error("Too many arguments") elif len(args) == 1: match = args[0] else: @@ -46,36 +48,46 @@ def main(): ubuntutools.misc.require_utf8() - for component in ('main', 'main-manual', - 'restricted', 'restricted-manual', - 'universe', 'universe-manual', - 'multiverse', 'multiverse-manual'): + for component in ( + "main", + "main-manual", + "restricted", + "restricted-manual", + "universe", + "universe-manual", + "multiverse", + "multiverse-manual", + ): - url = 'https://merges.ubuntu.com/%s.json' % component + url = "https://merges.ubuntu.com/%s.json" % component try: headers, page = Http().request(url) except HttpLib2Error as e: Logger.exception(e) sys.exit(1) if headers.status != 200: - Logger.error("%s: %s %s" % (url, headers.status, - headers.reason)) + Logger.error("%s: %s %s" % (url, headers.status, headers.reason)) sys.exit(1) for merge in json.loads(page): - package = merge['source_package'] - author, uploader = '', '' - if merge.get('user'): - author = merge['user'] - if merge.get('uploader'): - uploader = '(%s)' % merge['uploader'] - teams = merge.get('teams', []) + package = merge["source_package"] + author, uploader = "", "" + if merge.get("user"): + author = merge["user"] + if merge.get("uploader"): + uploader = "(%s)" % merge["uploader"] + teams = merge.get("teams", []) - pretty_uploader = '{} {}'.format(author, uploader) - if (match is None or match in package or match in author - or match in uploader or match in teams): - Logger.info('%s\t%s' % (package, pretty_uploader)) + pretty_uploader = "{} {}".format(author, uploader) + if ( + match is None + or match in package + or match in author + or match in uploader + or match in teams + ): + Logger.info("%s\t%s" % (package, pretty_uploader)) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/import-bug-from-debian b/import-bug-from-debian index 9aeac46..9553a29 100755 --- a/import-bug-from-debian +++ b/import-bug-from-debian @@ -33,6 +33,7 @@ from launchpadlib.launchpad import Launchpad from ubuntutools.config import UDTConfig from ubuntutools import getLogger + Logger = getLogger() @@ -40,19 +41,33 @@ def main(): bug_re = re.compile(r"bug=(\d+)") parser = argparse.ArgumentParser() - parser.add_argument("-b", "--browserless", action="store_true", - help="Don't open the bug in the browser at the end") - parser.add_argument("-l", "--lpinstance", metavar="INSTANCE", - help="LP instance to connect to (default: production)") - parser.add_argument("-v", "--verbose", action="store_true", - help="Print info about the bug being imported") - parser.add_argument("-n", "--dry-run", action="store_true", - help="Don't actually open a bug (also sets verbose)") - parser.add_argument("-p", "--package", - help="Launchpad package to file bug against " - "(default: Same as Debian)") - parser.add_argument("--no-conf", action="store_true", - help="Don't read config files or environment variables.") + parser.add_argument( + "-b", + "--browserless", + action="store_true", + help="Don't open the bug in the browser at the end", + ) + parser.add_argument( + "-l", + "--lpinstance", + metavar="INSTANCE", + help="LP instance to connect to (default: production)", + ) + parser.add_argument( + "-v", "--verbose", action="store_true", help="Print info about the bug being imported" + ) + parser.add_argument( + "-n", + "--dry-run", + action="store_true", + help="Don't actually open a bug (also sets verbose)", + ) + parser.add_argument( + "-p", "--package", help="Launchpad package to file bug against (default: Same as Debian)" + ) + parser.add_argument( + "--no-conf", action="store_true", help="Don't read config files or environment variables." + ) parser.add_argument("bugs", nargs="+", help="Bug number(s) or URL(s)") options = parser.parse_args() @@ -69,9 +84,9 @@ def main(): if options.verbose: Logger.setLevel(logging.DEBUG) - debian = launchpad.distributions['debian'] - ubuntu = launchpad.distributions['ubuntu'] - lp_debbugs = launchpad.bug_trackers.getByName(name='debbugs') + debian = launchpad.distributions["debian"] + ubuntu = launchpad.distributions["ubuntu"] + lp_debbugs = launchpad.bug_trackers.getByName(name="debbugs") bug_nums = [] @@ -101,31 +116,34 @@ def main(): bug_num = bug.bug_num subject = bug.subject log = debianbts.get_bug_log(bug_num) - summary = log[0]['message'].get_payload() + summary = log[0]["message"].get_payload() target = ubuntu.getSourcePackage(name=ubupackage) if target is None: - Logger.error("Source package '%s' is not in Ubuntu. Please specify " - "the destination source package with --package", - ubupackage) + Logger.error( + "Source package '%s' is not in Ubuntu. Please specify " + "the destination source package with --package", + ubupackage, + ) err = True continue - description = ('Imported from Debian bug http://bugs.debian.org/%d:\n\n%s' % - (bug_num, summary)) + description = "Imported from Debian bug http://bugs.debian.org/%d:\n\n%s" % ( + bug_num, + summary, + ) # LP limits descriptions to 50K chars - description = (description[:49994] + ' [...]') if len(description) > 50000 else description + description = (description[:49994] + " [...]") if len(description) > 50000 else description - Logger.debug('Target: %s' % target) - Logger.debug('Subject: %s' % subject) - Logger.debug('Description: ') + Logger.debug("Target: %s" % target) + Logger.debug("Subject: %s" % subject) + Logger.debug("Description: ") Logger.debug(description) if options.dry_run: - Logger.info('Dry-Run: not creating Ubuntu bug.') + Logger.info("Dry-Run: not creating Ubuntu bug.") continue - u_bug = launchpad.bugs.createBug(target=target, title=subject, - description=description) + u_bug = launchpad.bugs.createBug(target=target, title=subject, description=description) d_sp = debian.getSourcePackage(name=package) if d_sp is None and options.package: d_sp = debian.getSourcePackage(name=options.package) @@ -141,5 +159,5 @@ def main(): sys.exit(1) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/merge-changelog b/merge-changelog index 6dd245c..4719b30 100755 --- a/merge-changelog +++ b/merge-changelog @@ -23,19 +23,23 @@ import sys from debian.changelog import Changelog from ubuntutools import getLogger + Logger = getLogger() def usage(exit_code=1): - Logger.info('''Usage: merge-changelog + Logger.info( + """Usage: merge-changelog merge-changelog takes two changelogs that once shared a common source, merges them back together, and prints the merged result to stdout. This is useful if you need to manually merge a ubuntu package with a new Debian release of the package. -''') +""" + ) sys.exit(exit_code) + ######################################################################## # Changelog Management ######################################################################## @@ -67,11 +71,11 @@ def merge_changelog(left_changelog, right_changelog): assert block.version == version - Logger.info(str(block).strip() + ('\n' if ci else '')) + Logger.info(str(block).strip() + ("\n" if ci else "")) def main(): - if len(sys.argv) > 1 and sys.argv[1] in ('-h', '--help'): + if len(sys.argv) > 1 and sys.argv[1] in ("-h", "--help"): usage(0) if len(sys.argv) != 3: usage(1) @@ -83,5 +87,5 @@ def main(): sys.exit(0) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/pbuilder-dist b/pbuilder-dist index 3dcff57..732a298 100755 --- a/pbuilder-dist +++ b/pbuilder-dist @@ -45,6 +45,7 @@ from ubuntutools.config import UDTConfig from ubuntutools.question import YesNoQuestion from ubuntutools import getLogger + Logger = getLogger() @@ -87,32 +88,32 @@ class PbuilderDist(object): self.chroot_string = None # Authentication method - self.auth = 'sudo' + self.auth = "sudo" # Builder self.builder = builder - self._debian_distros = DebianDistroInfo().all + \ - ['stable', 'testing', 'unstable'] + self._debian_distros = DebianDistroInfo().all + ["stable", "testing", "unstable"] # Ensure that the used builder is installed - paths = set(os.environ['PATH'].split(':')) - paths |= set(('/sbin', '/usr/sbin', '/usr/local/sbin')) + paths = set(os.environ["PATH"].split(":")) + paths |= set(("/sbin", "/usr/sbin", "/usr/local/sbin")) if not any(os.path.exists(os.path.join(p, builder)) for p in paths): Logger.error('Could not find "%s".', builder) sys.exit(1) ############################################################## - self.base = os.path.expanduser(os.environ.get('PBUILDFOLDER', - '~/pbuilder/')) + self.base = os.path.expanduser(os.environ.get("PBUILDFOLDER", "~/pbuilder/")) - if 'SUDO_USER' in os.environ: - Logger.warning('Running under sudo. ' - 'This is probably not what you want. ' - 'pbuilder-dist will use sudo itself, ' - 'when necessary.') - if os.stat(os.environ['HOME']).st_uid != os.getuid(): + if "SUDO_USER" in os.environ: + Logger.warning( + "Running under sudo. " + "This is probably not what you want. " + "pbuilder-dist will use sudo itself, " + "when necessary." + ) + if os.stat(os.environ["HOME"]).st_uid != os.getuid(): Logger.error("You don't own $HOME") sys.exit(1) @@ -123,8 +124,8 @@ class PbuilderDist(object): Logger.error('Cannot create base directory "%s"', self.base) sys.exit(1) - if 'PBUILDAUTH' in os.environ: - self.auth = os.environ['PBUILDAUTH'] + if "PBUILDAUTH" in os.environ: + self.auth = os.environ["PBUILDAUTH"] self.system_architecture = ubuntutools.misc.host_architecture() self.system_distro = ubuntutools.misc.system_distribution() @@ -134,7 +135,7 @@ class PbuilderDist(object): self.target_distro = self.system_distro def set_target_distro(self, distro): - """ PbuilderDist.set_target_distro(distro) -> None + """PbuilderDist.set_target_distro(distro) -> None Check if the given target distribution name is correct, if it isn't know to the system ask the user for confirmation before @@ -145,16 +146,16 @@ class PbuilderDist(object): Logger.error('"%s" is an invalid distribution codename.', distro) sys.exit(1) - if not os.path.isfile(os.path.join('/usr/share/debootstrap/scripts/', - distro)): - if os.path.isdir('/usr/share/debootstrap/scripts/'): + if not os.path.isfile(os.path.join("/usr/share/debootstrap/scripts/", distro)): + if os.path.isdir("/usr/share/debootstrap/scripts/"): # Debian experimental doesn't have a debootstrap file but # should work nevertheless. if distro not in self._debian_distros: - question = ('Warning: Unknown distribution "%s". ' - 'Do you want to continue' % distro) - answer = YesNoQuestion().ask(question, 'no') - if answer == 'no': + question = ( + 'Warning: Unknown distribution "%s". ' "Do you want to continue" % distro + ) + answer = YesNoQuestion().ask(question, "no") + if answer == "no": sys.exit(0) else: Logger.error('Please install package "debootstrap".') @@ -163,33 +164,35 @@ class PbuilderDist(object): self.target_distro = distro def set_operation(self, operation): - """ PbuilderDist.set_operation -> None + """PbuilderDist.set_operation -> None Check if the given string is a valid pbuilder operation and depending on this either save it into the appropiate variable or finalize pbuilder-dist's execution. """ - arguments = ('create', 'update', 'build', 'clean', 'login', 'execute') + arguments = ("create", "update", "build", "clean", "login", "execute") if operation not in arguments: - if operation.endswith('.dsc'): + if operation.endswith(".dsc"): if os.path.isfile(operation): - self.operation = 'build' + self.operation = "build" return [operation] else: Logger.error('Could not find file "%s".', operation) sys.exit(1) else: - Logger.error('"%s" is not a recognized argument.\n' - 'Please use one of these: %s.', - operation, ', '.join(arguments)) + Logger.error( + '"%s" is not a recognized argument.\nPlease use one of these: %s.', + operation, + ", ".join(arguments), + ) sys.exit(1) else: self.operation = operation return [] def get_command(self, remaining_arguments=None): - """ PbuilderDist.get_command -> string + """PbuilderDist.get_command -> string Generate the pbuilder command which matches the given configuration and return it as a string. @@ -200,30 +203,34 @@ class PbuilderDist(object): if self.build_architecture == self.system_architecture: self.chroot_string = self.target_distro else: - self.chroot_string = (self.target_distro + '-' - + self.build_architecture) + self.chroot_string = self.target_distro + "-" + self.build_architecture prefix = os.path.join(self.base, self.chroot_string) - if '--buildresult' not in remaining_arguments: - result = os.path.normpath('%s_result/' % prefix) + if "--buildresult" not in remaining_arguments: + result = os.path.normpath("%s_result/" % prefix) else: - location_of_arg = remaining_arguments.index('--buildresult') - result = os.path.normpath(remaining_arguments[location_of_arg+1]) - remaining_arguments.pop(location_of_arg+1) + location_of_arg = remaining_arguments.index("--buildresult") + result = os.path.normpath(remaining_arguments[location_of_arg + 1]) + remaining_arguments.pop(location_of_arg + 1) remaining_arguments.pop(location_of_arg) - if not self.logfile and self.operation != 'login': - if self.operation == 'build': - dsc_files = [a for a in remaining_arguments - if a.strip().endswith('.dsc')] + if not self.logfile and self.operation != "login": + if self.operation == "build": + dsc_files = [a for a in remaining_arguments if a.strip().endswith(".dsc")] assert len(dsc_files) == 1 dsc = debian.deb822.Dsc(open(dsc_files[0])) - version = ubuntutools.version.Version(dsc['Version']) - name = (dsc['Source'] + '_' + version.strip_epoch() + '_' + - self.build_architecture + '.build') + version = ubuntutools.version.Version(dsc["Version"]) + name = ( + dsc["Source"] + + "_" + + version.strip_epoch() + + "_" + + self.build_architecture + + ".build" + ) self.logfile = os.path.join(result, name) else: - self.logfile = os.path.join(result, 'last_operation.log') + self.logfile = os.path.join(result, "last_operation.log") if not os.path.isdir(result): try: @@ -233,87 +240,89 @@ class PbuilderDist(object): sys.exit(1) arguments = [ - '--%s' % self.operation, - '--distribution', self.target_distro, - '--buildresult', result, + "--%s" % self.operation, + "--distribution", + self.target_distro, + "--buildresult", + result, ] - if self.operation == 'update': - arguments += ['--override-config'] + if self.operation == "update": + arguments += ["--override-config"] - if self.builder == 'pbuilder': - arguments += ['--basetgz', prefix + '-base.tgz'] - elif self.builder == 'cowbuilder': - arguments += ['--basepath', prefix + '-base.cow'] + if self.builder == "pbuilder": + arguments += ["--basetgz", prefix + "-base.tgz"] + elif self.builder == "cowbuilder": + arguments += ["--basepath", prefix + "-base.cow"] else: Logger.error('Unrecognized builder "%s".', self.builder) sys.exit(1) if self.logfile: - arguments += ['--logfile', self.logfile] + arguments += ["--logfile", self.logfile] - if os.path.exists('/var/cache/archive/'): - arguments += ['--bindmounts', '/var/cache/archive/'] + if os.path.exists("/var/cache/archive/"): + arguments += ["--bindmounts", "/var/cache/archive/"] config = UDTConfig() if self.target_distro in self._debian_distros: - mirror = os.environ.get('MIRRORSITE', - config.get_value('DEBIAN_MIRROR')) - components = 'main' + mirror = os.environ.get("MIRRORSITE", config.get_value("DEBIAN_MIRROR")) + components = "main" if self.extra_components: - components += ' contrib non-free' + components += " contrib non-free" else: - mirror = os.environ.get('MIRRORSITE', - config.get_value('UBUNTU_MIRROR')) - if self.build_architecture not in ('amd64', 'i386'): - mirror = os.environ.get( - 'MIRRORSITE', config.get_value('UBUNTU_PORTS_MIRROR')) - components = 'main restricted' + mirror = os.environ.get("MIRRORSITE", config.get_value("UBUNTU_MIRROR")) + if self.build_architecture not in ("amd64", "i386"): + mirror = os.environ.get("MIRRORSITE", config.get_value("UBUNTU_PORTS_MIRROR")) + components = "main restricted" if self.extra_components: - components += ' universe multiverse' + components += " universe multiverse" - arguments += ['--mirror', mirror] + arguments += ["--mirror", mirror] othermirrors = [] - localrepo = '/var/cache/archive/' + self.target_distro + localrepo = "/var/cache/archive/" + self.target_distro if os.path.exists(localrepo): - repo = 'deb file:///var/cache/archive/ %s/' % self.target_distro + repo = "deb file:///var/cache/archive/ %s/" % self.target_distro othermirrors.append(repo) if self.target_distro in self._debian_distros: debian_info = DebianDistroInfo() try: - codename = debian_info.codename(self.target_distro, - default=self.target_distro) + codename = debian_info.codename(self.target_distro, default=self.target_distro) except DistroDataOutdated as error: Logger.warning(error) - if codename in (debian_info.devel(), 'experimental'): + if codename in (debian_info.devel(), "experimental"): self.enable_security = False self.enable_updates = False self.enable_proposed = False - elif codename in (debian_info.testing(), 'testing'): + elif codename in (debian_info.testing(), "testing"): self.enable_updates = False if self.enable_security: - pocket = '-security' + pocket = "-security" with suppress(ValueError): # before bullseye (version 11) security suite is /updates if float(debian_info.version(codename)) < 11.0: - pocket = '/updates' - othermirrors.append('deb %s %s%s %s' - % (config.get_value('DEBSEC_MIRROR'), - self.target_distro, pocket, components)) + pocket = "/updates" + othermirrors.append( + "deb %s %s%s %s" + % (config.get_value("DEBSEC_MIRROR"), self.target_distro, pocket, components) + ) if self.enable_updates: - othermirrors.append('deb %s %s-updates %s' - % (mirror, self.target_distro, components)) + othermirrors.append( + "deb %s %s-updates %s" % (mirror, self.target_distro, components) + ) if self.enable_proposed: - othermirrors.append('deb %s %s-proposed-updates %s' - % (mirror, self.target_distro, components)) + othermirrors.append( + "deb %s %s-proposed-updates %s" % (mirror, self.target_distro, components) + ) if self.enable_backports: - othermirrors.append('deb %s %s-backports %s' - % (mirror, self.target_distro, components)) + othermirrors.append( + "deb %s %s-backports %s" % (mirror, self.target_distro, components) + ) - aptcache = os.path.join(self.base, 'aptcache', 'debian') + aptcache = os.path.join(self.base, "aptcache", "debian") else: try: dev_release = self.target_distro == UbuntuDistroInfo().devel() @@ -326,46 +335,51 @@ class PbuilderDist(object): self.enable_updates = False if self.enable_security: - othermirrors.append('deb %s %s-security %s' - % (mirror, self.target_distro, components)) + othermirrors.append( + "deb %s %s-security %s" % (mirror, self.target_distro, components) + ) if self.enable_updates: - othermirrors.append('deb %s %s-updates %s' - % (mirror, self.target_distro, components)) + othermirrors.append( + "deb %s %s-updates %s" % (mirror, self.target_distro, components) + ) if self.enable_proposed: - othermirrors.append('deb %s %s-proposed %s' - % (mirror, self.target_distro, components)) + othermirrors.append( + "deb %s %s-proposed %s" % (mirror, self.target_distro, components) + ) - aptcache = os.path.join(self.base, 'aptcache', 'ubuntu') + aptcache = os.path.join(self.base, "aptcache", "ubuntu") - if 'OTHERMIRROR' in os.environ: - othermirrors += os.environ['OTHERMIRROR'].split('|') + if "OTHERMIRROR" in os.environ: + othermirrors += os.environ["OTHERMIRROR"].split("|") if othermirrors: - arguments += ['--othermirror', '|'.join(othermirrors)] + arguments += ["--othermirror", "|".join(othermirrors)] # Work around LP:#599695 - if (ubuntutools.misc.system_distribution() == 'Debian' - and self.target_distro not in self._debian_distros): - if not os.path.exists( - '/usr/share/keyrings/ubuntu-archive-keyring.gpg'): - Logger.error('ubuntu-keyring not installed') + if ( + ubuntutools.misc.system_distribution() == "Debian" + and self.target_distro not in self._debian_distros + ): + if not os.path.exists("/usr/share/keyrings/ubuntu-archive-keyring.gpg"): + Logger.error("ubuntu-keyring not installed") sys.exit(1) arguments += [ - '--debootstrapopts', - '--keyring=/usr/share/keyrings/ubuntu-archive-keyring.gpg', + "--debootstrapopts", + "--keyring=/usr/share/keyrings/ubuntu-archive-keyring.gpg", ] - elif (ubuntutools.misc.system_distribution() == 'Ubuntu' - and self.target_distro in self._debian_distros): - if not os.path.exists( - '/usr/share/keyrings/debian-archive-keyring.gpg'): - Logger.error('debian-archive-keyring not installed') + elif ( + ubuntutools.misc.system_distribution() == "Ubuntu" + and self.target_distro in self._debian_distros + ): + if not os.path.exists("/usr/share/keyrings/debian-archive-keyring.gpg"): + Logger.error("debian-archive-keyring not installed") sys.exit(1) arguments += [ - '--debootstrapopts', - '--keyring=/usr/share/keyrings/debian-archive-keyring.gpg', + "--debootstrapopts", + "--keyring=/usr/share/keyrings/debian-archive-keyring.gpg", ] - arguments += ['--aptcache', aptcache, '--components', components] + arguments += ["--aptcache", aptcache, "--components", components] if not os.path.isdir(aptcache): try: @@ -375,13 +389,11 @@ class PbuilderDist(object): sys.exit(1) if self.build_architecture != self.system_architecture: - arguments += ['--debootstrapopts', - '--arch=' + self.build_architecture] + arguments += ["--debootstrapopts", "--arch=" + self.build_architecture] - apt_conf_dir = os.path.join(self.base, - 'etc/%s/apt.conf' % self.target_distro) + apt_conf_dir = os.path.join(self.base, "etc/%s/apt.conf" % self.target_distro) if os.path.exists(apt_conf_dir): - arguments += ['--aptconfdir', apt_conf_dir] + arguments += ["--aptconfdir", apt_conf_dir] # Append remaining arguments if remaining_arguments: @@ -392,28 +404,28 @@ class PbuilderDist(object): # With both common variable name schemes (BTS: #659060). return [ self.auth, - 'HOME=' + os.path.expanduser('~'), - 'ARCHITECTURE=' + self.build_architecture, - 'DISTRIBUTION=' + self.target_distro, - 'ARCH=' + self.build_architecture, - 'DIST=' + self.target_distro, - 'DEB_BUILD_OPTIONS=' + os.environ.get('DEB_BUILD_OPTIONS', ''), + "HOME=" + os.path.expanduser("~"), + "ARCHITECTURE=" + self.build_architecture, + "DISTRIBUTION=" + self.target_distro, + "ARCH=" + self.build_architecture, + "DIST=" + self.target_distro, + "DEB_BUILD_OPTIONS=" + os.environ.get("DEB_BUILD_OPTIONS", ""), self.builder, ] + arguments def show_help(exit_code=0): - """ help() -> None + """help() -> None Print a help message for pbuilder-dist, and exit with the given code. """ - Logger.info('See man pbuilder-dist for more information.') + Logger.info("See man pbuilder-dist for more information.") sys.exit(exit_code) def main(): - """ main() -> None + """main() -> None This is pbuilder-dist's main function. It creates a PbuilderDist object, modifies all necessary settings taking data from the @@ -421,27 +433,25 @@ def main(): the script and runs pbuilder itself or exists with an error message. """ script_name = os.path.basename(sys.argv[0]) - parts = script_name.split('-') + parts = script_name.split("-") # Copy arguments into another list for save manipulation args = sys.argv[1:] - if ('-' in script_name and parts[0] not in ('pbuilder', 'cowbuilder') - or len(parts) > 3): - Logger.error('"%s" is not a valid name for a "pbuilder-dist" ' - 'executable.', script_name) + if "-" in script_name and parts[0] not in ("pbuilder", "cowbuilder") or len(parts) > 3: + Logger.error('"%s" is not a valid name for a "pbuilder-dist" executable.', script_name) sys.exit(1) if len(args) < 1: - Logger.error('Insufficient number of arguments.') + Logger.error("Insufficient number of arguments.") show_help(1) - if args[0] in ('-h', '--help', 'help'): + if args[0] in ("-h", "--help", "help"): show_help(0) app = PbuilderDist(parts[0]) - if len(parts) > 1 and parts[1] != 'dist' and '.' not in parts[1]: + if len(parts) > 1 and parts[1] != "dist" and "." not in parts[1]: app.set_target_distro(parts[1]) else: app.set_target_distro(args.pop(0)) @@ -449,24 +459,28 @@ def main(): if len(parts) > 2: requested_arch = parts[2] elif len(args) > 0: - if shutil.which('arch-test') is not None: - if subprocess.run( - ['arch-test', args[0]], - stdout=subprocess.DEVNULL).returncode == 0: + if shutil.which("arch-test") is not None: + if subprocess.run(["arch-test", args[0]], stdout=subprocess.DEVNULL).returncode == 0: requested_arch = args.pop(0) - elif (os.path.isdir('/usr/lib/arch-test') - and args[0] in os.listdir('/usr/lib/arch-test/')): - Logger.error('Architecture "%s" is not supported on your ' - 'currently running kernel. Consider installing ' - 'the qemu-user-static package to enable the use of ' - 'foreign architectures.', args[0]) + elif os.path.isdir("/usr/lib/arch-test") and args[0] in os.listdir( + "/usr/lib/arch-test/" + ): + Logger.error( + 'Architecture "%s" is not supported on your ' + "currently running kernel. Consider installing " + "the qemu-user-static package to enable the use of " + "foreign architectures.", + args[0], + ) sys.exit(1) else: requested_arch = None else: - Logger.error('Cannot determine if "%s" is a valid architecture. ' - 'Please install the arch-test package and retry.', - args[0]) + Logger.error( + 'Cannot determine if "%s" is a valid architecture. ' + "Please install the arch-test package and retry.", + args[0], + ) sys.exit(1) else: requested_arch = None @@ -474,62 +488,74 @@ def main(): if requested_arch: app.build_architecture = requested_arch # For some foreign architectures we need to use qemu - if (requested_arch != app.system_architecture - and (app.system_architecture, requested_arch) not in [ - ('amd64', 'i386'), ('amd64', 'lpia'), ('arm', 'armel'), - ('armel', 'arm'), ('armel', 'armhf'), ('armhf', 'armel'), - ('arm64', 'arm'), ('arm64', 'armhf'), ('arm64', 'armel'), - ('i386', 'lpia'), ('lpia', 'i386'), ('powerpc', 'ppc64'), - ('ppc64', 'powerpc'), ('sparc', 'sparc64'), - ('sparc64', 'sparc')]): - args += ['--debootstrap', 'qemu-debootstrap'] + if requested_arch != app.system_architecture and ( + app.system_architecture, + requested_arch, + ) not in [ + ("amd64", "i386"), + ("amd64", "lpia"), + ("arm", "armel"), + ("armel", "arm"), + ("armel", "armhf"), + ("armhf", "armel"), + ("arm64", "arm"), + ("arm64", "armhf"), + ("arm64", "armel"), + ("i386", "lpia"), + ("lpia", "i386"), + ("powerpc", "ppc64"), + ("ppc64", "powerpc"), + ("sparc", "sparc64"), + ("sparc64", "sparc"), + ]: + args += ["--debootstrap", "qemu-debootstrap"] - if 'mainonly' in sys.argv or '--main-only' in sys.argv: + if "mainonly" in sys.argv or "--main-only" in sys.argv: app.extra_components = False - if 'mainonly' in sys.argv: - args.remove('mainonly') + if "mainonly" in sys.argv: + args.remove("mainonly") else: - args.remove('--main-only') + args.remove("--main-only") - if '--release-only' in sys.argv: - args.remove('--release-only') + if "--release-only" in sys.argv: + args.remove("--release-only") app.enable_security = False app.enable_updates = False app.enable_proposed = False - elif '--security-only' in sys.argv: - args.remove('--security-only') + elif "--security-only" in sys.argv: + args.remove("--security-only") app.enable_updates = False app.enable_proposed = False - elif '--updates-only' in sys.argv: - args.remove('--updates-only') + elif "--updates-only" in sys.argv: + args.remove("--updates-only") app.enable_proposed = False - elif '--backports' in sys.argv: - args.remove('--backports') + elif "--backports" in sys.argv: + args.remove("--backports") app.enable_backports = True if len(args) < 1: - Logger.error('Insufficient number of arguments.') + Logger.error("Insufficient number of arguments.") show_help(1) # Parse the operation args = app.set_operation(args.pop(0)) + args - if app.operation == 'build': - if len([a for a in args if a.strip().endswith('.dsc')]) != 1: - msg = 'You have to specify one .dsc file if you want to build.' + if app.operation == "build": + if len([a for a in args if a.strip().endswith(".dsc")]) != 1: + msg = "You have to specify one .dsc file if you want to build." Logger.error(msg) sys.exit(1) # Execute the pbuilder command - if '--debug-echo' not in args: + if "--debug-echo" not in args: sys.exit(subprocess.call(app.get_command(args))) else: - Logger.info(app.get_command([arg for arg in args if arg != '--debug-echo'])) + Logger.info(app.get_command([arg for arg in args if arg != "--debug-echo"])) -if __name__ == '__main__': +if __name__ == "__main__": try: main() except KeyboardInterrupt: - Logger.error('Manually aborted.') + Logger.error("Manually aborted.") sys.exit(1) diff --git a/pull-debian-ddebs b/pull-debian-ddebs index 330f315..9a5b709 100755 --- a/pull-debian-ddebs +++ b/pull-debian-ddebs @@ -7,5 +7,5 @@ from ubuntutools.pullpkg import PullPkg -if __name__ == '__main__': - PullPkg.main(distro='debian', pull='ddebs') +if __name__ == "__main__": + PullPkg.main(distro="debian", pull="ddebs") diff --git a/pull-debian-debdiff b/pull-debian-debdiff index 365b7f8..6731c96 100755 --- a/pull-debian-debdiff +++ b/pull-debian-debdiff @@ -27,19 +27,20 @@ from ubuntutools.config import UDTConfig from ubuntutools.version import Version from ubuntutools import getLogger + Logger = getLogger() def previous_version(package, version, distance): "Given an (extracted) package, determine the version distance versions ago" upver = Version(version).upstream_version - filename = '%s-%s/debian/changelog' % (package, upver) - changelog_file = open(filename, 'r') + filename = "%s-%s/debian/changelog" % (package, upver) + changelog_file = open(filename, "r") changelog = debian.changelog.Changelog(changelog_file.read()) changelog_file.close() seen = 0 for entry in changelog: - if entry.distributions == 'UNRELEASED': + if entry.distributions == "UNRELEASED": continue if seen == distance: return entry.version.full_version @@ -48,46 +49,60 @@ def previous_version(package, version, distance): def main(): - parser = optparse.OptionParser('%prog [options] ' - '[distance]') - parser.add_option('-f', '--fetch', - dest='fetch_only', default=False, action='store_true', - help="Only fetch the source packages, don't diff.") - parser.add_option('-d', '--debian-mirror', metavar='DEBIAN_MIRROR', - dest='debian_mirror', - help='Preferred Debian mirror ' - '(default: http://deb.debian.org/debian)') - parser.add_option('-s', '--debsec-mirror', metavar='DEBSEC_MIRROR', - dest='debsec_mirror', - help='Preferred Debian Security mirror ' - '(default: http://security.debian.org)') - parser.add_option('--no-conf', - dest='no_conf', default=False, action='store_true', - help="Don't read config files or environment variables") + parser = optparse.OptionParser("%prog [options] [distance]") + parser.add_option( + "-f", + "--fetch", + dest="fetch_only", + default=False, + action="store_true", + help="Only fetch the source packages, don't diff.", + ) + parser.add_option( + "-d", + "--debian-mirror", + metavar="DEBIAN_MIRROR", + dest="debian_mirror", + help="Preferred Debian mirror (default: http://deb.debian.org/debian)", + ) + parser.add_option( + "-s", + "--debsec-mirror", + metavar="DEBSEC_MIRROR", + dest="debsec_mirror", + help="Preferred Debian Security mirror (default: http://security.debian.org)", + ) + parser.add_option( + "--no-conf", + dest="no_conf", + default=False, + action="store_true", + help="Don't read config files or environment variables", + ) opts, args = parser.parse_args() if len(args) < 2: - parser.error('Must specify package and version') + parser.error("Must specify package and version") elif len(args) > 3: - parser.error('Too many arguments') + parser.error("Too many arguments") package = args[0] version = args[1] distance = int(args[2]) if len(args) > 2 else 1 config = UDTConfig(opts.no_conf) if opts.debian_mirror is None: - opts.debian_mirror = config.get_value('DEBIAN_MIRROR') + opts.debian_mirror = config.get_value("DEBIAN_MIRROR") if opts.debsec_mirror is None: - opts.debsec_mirror = config.get_value('DEBSEC_MIRROR') + opts.debsec_mirror = config.get_value("DEBSEC_MIRROR") mirrors = [opts.debsec_mirror, opts.debian_mirror] - Logger.info('Downloading %s %s', package, version) + Logger.info("Downloading %s %s", package, version) newpkg = DebianSourcePackage(package, version, mirrors=mirrors) try: newpkg.pull() except DownloadError as e: - Logger.error('Failed to download: %s', str(e)) + Logger.error("Failed to download: %s", str(e)) sys.exit(1) newpkg.unpack() @@ -96,21 +111,21 @@ def main(): oldversion = previous_version(package, version, distance) if not oldversion: - Logger.error('No previous version could be found') + Logger.error("No previous version could be found") sys.exit(1) - Logger.info('Downloading %s %s', package, oldversion) + Logger.info("Downloading %s %s", package, oldversion) oldpkg = DebianSourcePackage(package, oldversion, mirrors=mirrors) try: oldpkg.pull() except DownloadError as e: - Logger.error('Failed to download: %s', str(e)) + Logger.error("Failed to download: %s", str(e)) sys.exit(1) - Logger.info('file://' + oldpkg.debdiff(newpkg, diffstat=True)) + Logger.info("file://" + oldpkg.debdiff(newpkg, diffstat=True)) -if __name__ == '__main__': +if __name__ == "__main__": try: main() except KeyboardInterrupt: - Logger.info('User abort.') + Logger.info("User abort.") diff --git a/pull-debian-debs b/pull-debian-debs index 4f92407..37fbc6c 100755 --- a/pull-debian-debs +++ b/pull-debian-debs @@ -7,5 +7,5 @@ from ubuntutools.pullpkg import PullPkg -if __name__ == '__main__': - PullPkg.main(distro='debian', pull='debs') +if __name__ == "__main__": + PullPkg.main(distro="debian", pull="debs") diff --git a/pull-debian-source b/pull-debian-source index 3ffb2dc..1b7ab5e 100755 --- a/pull-debian-source +++ b/pull-debian-source @@ -7,5 +7,5 @@ from ubuntutools.pullpkg import PullPkg -if __name__ == '__main__': - PullPkg.main(distro='debian', pull='source') +if __name__ == "__main__": + PullPkg.main(distro="debian", pull="source") diff --git a/pull-debian-udebs b/pull-debian-udebs index b839f88..0ba5161 100755 --- a/pull-debian-udebs +++ b/pull-debian-udebs @@ -7,5 +7,5 @@ from ubuntutools.pullpkg import PullPkg -if __name__ == '__main__': - PullPkg.main(distro='debian', pull='udebs') +if __name__ == "__main__": + PullPkg.main(distro="debian", pull="udebs") diff --git a/pull-lp-ddebs b/pull-lp-ddebs index 16aab20..589e5cd 100755 --- a/pull-lp-ddebs +++ b/pull-lp-ddebs @@ -7,5 +7,5 @@ from ubuntutools.pullpkg import PullPkg -if __name__ == '__main__': - PullPkg.main(distro='ubuntu', pull='ddebs') +if __name__ == "__main__": + PullPkg.main(distro="ubuntu", pull="ddebs") diff --git a/pull-lp-debs b/pull-lp-debs index 33ad826..d47c5d6 100755 --- a/pull-lp-debs +++ b/pull-lp-debs @@ -7,5 +7,5 @@ from ubuntutools.pullpkg import PullPkg -if __name__ == '__main__': - PullPkg.main(distro='ubuntu', pull='debs') +if __name__ == "__main__": + PullPkg.main(distro="ubuntu", pull="debs") diff --git a/pull-lp-source b/pull-lp-source index 6b399fd..92e45a0 100755 --- a/pull-lp-source +++ b/pull-lp-source @@ -7,5 +7,5 @@ from ubuntutools.pullpkg import PullPkg -if __name__ == '__main__': - PullPkg.main(distro='ubuntu', pull='source') +if __name__ == "__main__": + PullPkg.main(distro="ubuntu", pull="source") diff --git a/pull-lp-udebs b/pull-lp-udebs index 580c65c..a8b847b 100755 --- a/pull-lp-udebs +++ b/pull-lp-udebs @@ -7,5 +7,5 @@ from ubuntutools.pullpkg import PullPkg -if __name__ == '__main__': - PullPkg.main(distro='ubuntu', pull='udebs') +if __name__ == "__main__": + PullPkg.main(distro="ubuntu", pull="udebs") diff --git a/pull-pkg b/pull-pkg index 8ed38b8..e0d5a2c 100755 --- a/pull-pkg +++ b/pull-pkg @@ -25,5 +25,5 @@ from ubuntutools.pullpkg import PullPkg -if __name__ == '__main__': +if __name__ == "__main__": PullPkg.main() diff --git a/pull-ppa-ddebs b/pull-ppa-ddebs index 8b9c3cf..b9ee641 100755 --- a/pull-ppa-ddebs +++ b/pull-ppa-ddebs @@ -8,5 +8,5 @@ from ubuntutools.pullpkg import PullPkg -if __name__ == '__main__': - PullPkg.main(distro='ppa', pull='ddebs') +if __name__ == "__main__": + PullPkg.main(distro="ppa", pull="ddebs") diff --git a/pull-ppa-debs b/pull-ppa-debs index 0804e29..89822a9 100755 --- a/pull-ppa-debs +++ b/pull-ppa-debs @@ -8,5 +8,5 @@ from ubuntutools.pullpkg import PullPkg -if __name__ == '__main__': - PullPkg.main(distro='ppa', pull='debs') +if __name__ == "__main__": + PullPkg.main(distro="ppa", pull="debs") diff --git a/pull-ppa-source b/pull-ppa-source index 065bb2b..97735b5 100755 --- a/pull-ppa-source +++ b/pull-ppa-source @@ -8,5 +8,5 @@ from ubuntutools.pullpkg import PullPkg -if __name__ == '__main__': - PullPkg.main(distro='ppa', pull='source') +if __name__ == "__main__": + PullPkg.main(distro="ppa", pull="source") diff --git a/pull-ppa-udebs b/pull-ppa-udebs index 4e59c3a..d57e987 100755 --- a/pull-ppa-udebs +++ b/pull-ppa-udebs @@ -8,5 +8,5 @@ from ubuntutools.pullpkg import PullPkg -if __name__ == '__main__': - PullPkg.main(distro='ppa', pull='udebs') +if __name__ == "__main__": + PullPkg.main(distro="ppa", pull="udebs") diff --git a/pull-uca-ddebs b/pull-uca-ddebs index bd55ac5..023a848 100755 --- a/pull-uca-ddebs +++ b/pull-uca-ddebs @@ -7,5 +7,5 @@ from ubuntutools.pullpkg import PullPkg -if __name__ == '__main__': - PullPkg.main(distro='uca', pull='ddebs') +if __name__ == "__main__": + PullPkg.main(distro="uca", pull="ddebs") diff --git a/pull-uca-debs b/pull-uca-debs index 096cf8f..a599973 100755 --- a/pull-uca-debs +++ b/pull-uca-debs @@ -7,5 +7,5 @@ from ubuntutools.pullpkg import PullPkg -if __name__ == '__main__': - PullPkg.main(distro='uca', pull='debs') +if __name__ == "__main__": + PullPkg.main(distro="uca", pull="debs") diff --git a/pull-uca-source b/pull-uca-source index 6aa6676..e174e7e 100755 --- a/pull-uca-source +++ b/pull-uca-source @@ -7,5 +7,5 @@ from ubuntutools.pullpkg import PullPkg -if __name__ == '__main__': - PullPkg.main(distro='uca', pull='source') +if __name__ == "__main__": + PullPkg.main(distro="uca", pull="source") diff --git a/pull-uca-udebs b/pull-uca-udebs index 95b0626..55b609e 100755 --- a/pull-uca-udebs +++ b/pull-uca-udebs @@ -7,5 +7,5 @@ from ubuntutools.pullpkg import PullPkg -if __name__ == '__main__': - PullPkg.main(distro='uca', pull='udebs') +if __name__ == "__main__": + PullPkg.main(distro="uca", pull="udebs") diff --git a/requestbackport b/requestbackport index 8f27c9a..99f8316 100755 --- a/requestbackport +++ b/requestbackport @@ -24,11 +24,11 @@ from distro_info import UbuntuDistroInfo from ubuntutools.config import UDTConfig from ubuntutools.lp.lpapicache import Launchpad, Distribution from ubuntutools.lp.udtexceptions import PackageNotFoundException -from ubuntutools.question import (YesNoQuestion, EditBugReport, - confirmation_prompt) +from ubuntutools.question import YesNoQuestion, EditBugReport, confirmation_prompt from ubuntutools.rdepends import query_rdepends, RDependsException from ubuntutools import getLogger + Logger = getLogger() @@ -44,11 +44,9 @@ def determine_destinations(source, destination): if source not in ubuntu_info.all: raise DestinationException("Source release %s does not exist" % source) if destination not in ubuntu_info.all: - raise DestinationException("Destination release %s does not exist" - % destination) + raise DestinationException("Destination release %s does not exist" % destination) if destination not in ubuntu_info.supported(): - raise DestinationException("Destination release %s is not supported" - % destination) + raise DestinationException("Destination release %s is not supported" % destination) found = False destinations = [] @@ -76,30 +74,34 @@ def determine_destinations(source, destination): def disclaimer(): - print("Ubuntu's backports are not for fixing bugs in stable releases, " - "but for bringing new features to older, stable releases.\n" - "See https://wiki.ubuntu.com/UbuntuBackports for the Ubuntu " - "Backports policy and processes.\n" - "See https://wiki.ubuntu.com/StableReleaseUpdates for the process " - "for fixing bugs in stable releases.") + print( + "Ubuntu's backports are not for fixing bugs in stable releases, " + "but for bringing new features to older, stable releases.\n" + "See https://wiki.ubuntu.com/UbuntuBackports for the Ubuntu " + "Backports policy and processes.\n" + "See https://wiki.ubuntu.com/StableReleaseUpdates for the process " + "for fixing bugs in stable releases." + ) confirmation_prompt() def check_existing(package): """Search for possible existing bug reports""" - distro = Distribution('ubuntu') + distro = Distribution("ubuntu") srcpkg = distro.getSourcePackage(name=package.getPackageName()) - bugs = srcpkg.searchTasks(omit_duplicates=True, - search_text="[BPO]", - status=["Incomplete", "New", "Confirmed", - "Triaged", "In Progress", - "Fix Committed"]) + bugs = srcpkg.searchTasks( + omit_duplicates=True, + search_text="[BPO]", + status=["Incomplete", "New", "Confirmed", "Triaged", "In Progress", "Fix Committed"], + ) if not bugs: return - Logger.info("There are existing bug reports that look similar to your " - "request. Please check before continuing:") + Logger.info( + "There are existing bug reports that look similar to your " + "request. Please check before continuing:" + ) for bug in sorted([bug_task.bug for bug_task in bugs], key=lambda bug: bug.id): Logger.info(" * LP: #%-7i: %s %s", bug.id, bug.title, bug.web_link) @@ -114,7 +116,7 @@ def find_rdepends(releases, published_binaries): for binpkg in published_binaries: intermediate[binpkg] - for arch in ('any', 'source'): + for arch in ("any", "source"): for release in releases: for binpkg in published_binaries: try: @@ -125,20 +127,20 @@ def find_rdepends(releases, published_binaries): for relationship, rdeps in raw_rdeps.items(): for rdep in rdeps: # Ignore circular deps: - if rdep['Package'] in published_binaries: + if rdep["Package"] in published_binaries: continue # arch==any queries return Reverse-Build-Deps: - if arch == 'any' and rdep.get('Architectures', []) == ['source']: + if arch == "any" and rdep.get("Architectures", []) == ["source"]: continue - intermediate[binpkg][rdep['Package']].append((release, relationship)) + intermediate[binpkg][rdep["Package"]].append((release, relationship)) output = [] for binpkg, rdeps in intermediate.items(): - output += ['', binpkg, '-' * len(binpkg)] + output += ["", binpkg, "-" * len(binpkg)] for pkg, appearences in rdeps.items(): - output += ['* %s' % pkg] + output += ["* %s" % pkg] for release, relationship in appearences: - output += [' [ ] %s (%s)' % (release, relationship)] + output += [" [ ] %s (%s)" % (release, relationship)] found_any = sum(len(rdeps) for rdeps in intermediate.values()) if found_any: @@ -153,8 +155,8 @@ def find_rdepends(releases, published_binaries): "package currently in the release still works with the new " "%(package)s installed. " "Reverse- Recommends, Suggests, and Enhances don't need to be " - "tested, and are listed for completeness-sake." - ] + output + "tested, and are listed for completeness-sake.", + ] + output else: output = ["No reverse dependencies"] @@ -162,8 +164,8 @@ def find_rdepends(releases, published_binaries): def locate_package(package, distribution): - archive = Distribution('ubuntu').getArchive() - for pass_ in ('source', 'binary'): + archive = Distribution("ubuntu").getArchive() + for pass_ in ("source", "binary"): try: package_spph = archive.getSourcePackage(package, distribution) return package_spph @@ -174,8 +176,9 @@ def locate_package(package, distribution): Logger.error(str(e)) sys.exit(1) package = apt_pkg.candidate.source_name - Logger.info("Binary package specified, considering its source " - "package instead: %s", package) + Logger.info( + "Binary package specified, considering its source package instead: %s", package + ) def request_backport(package_spph, source, destinations): @@ -184,67 +187,77 @@ def request_backport(package_spph, source, destinations): published_binaries.add(bpph.getPackageName()) if not published_binaries: - Logger.error("%s (%s) has no published binaries in %s. ", - package_spph.getPackageName(), package_spph.getVersion(), - source) - Logger.info("Is it stuck in bin-NEW? It can't be backported until " - "the binaries have been accepted.") + Logger.error( + "%s (%s) has no published binaries in %s. ", + package_spph.getPackageName(), + package_spph.getVersion(), + source, + ) + Logger.info( + "Is it stuck in bin-NEW? It can't be backported until " + "the binaries have been accepted." + ) sys.exit(1) testing = ["[Testing]", ""] for dest in destinations: testing += [" * %s:" % dest.capitalize()] testing += [" [ ] Package builds without modification"] - testing += [" [ ] %s installs cleanly and runs" % binary - for binary in published_binaries] + testing += [ + " [ ] %s installs cleanly and runs" % binary for binary in published_binaries + ] subst = { - 'package': package_spph.getPackageName(), - 'version': package_spph.getVersion(), - 'component': package_spph.getComponent(), - 'source': package_spph.getSeriesAndPocket(), - 'destinations': ', '.join(destinations), + "package": package_spph.getPackageName(), + "version": package_spph.getVersion(), + "component": package_spph.getComponent(), + "source": package_spph.getSeriesAndPocket(), + "destinations": ", ".join(destinations), } subject = "[BPO] %(package)s %(version)s to %(destinations)s" % subst - body = ('\n'.join( + body = ( + "\n".join( [ - "[Impact]", - "", - " * Justification for backporting the new version to the stable release.", - "", - "[Scope]", - "", - " * List the Ubuntu release you will backport from," - " and the specific package version.", - "", - " * List the Ubuntu release(s) you will backport to.", - "", - "[Other Info]", - "", - " * Anything else you think is useful to include", - "" + "[Impact]", + "", + " * Justification for backporting the new version to the stable release.", + "", + "[Scope]", + "", + " * List the Ubuntu release you will backport from," + " and the specific package version.", + "", + " * List the Ubuntu release(s) you will backport to.", + "", + "[Other Info]", + "", + " * Anything else you think is useful to include", + "", ] + testing + [""] + find_rdepends(destinations, published_binaries) - + [""]) % subst) + + [""] + ) + % subst + ) editor = EditBugReport(subject, body) editor.edit() subject, body = editor.get_report() - Logger.info('The final report is:\nSummary: %s\nDescription:\n%s\n', - subject, body) + Logger.info("The final report is:\nSummary: %s\nDescription:\n%s\n", subject, body) if YesNoQuestion().ask("Request this backport", "yes") == "no": sys.exit(1) - distro = Distribution('ubuntu') + distro = Distribution("ubuntu") pkgname = package_spph.getPackageName() - bug = Launchpad.bugs.createBug(title=subject, description=body, - target=distro.getSourcePackage(name=pkgname)) + bug = Launchpad.bugs.createBug( + title=subject, description=body, target=distro.getSourcePackage(name=pkgname) + ) - bug.subscribe(person=Launchpad.people['ubuntu-backporters']) + bug.subscribe(person=Launchpad.people["ubuntu-backporters"]) for dest in destinations: series = distro.getSeries(dest) @@ -257,20 +270,35 @@ def request_backport(package_spph, source, destinations): def main(): - parser = optparse.OptionParser('%prog [options] package') - parser.add_option('-d', '--destination', metavar='DEST', - help='Backport to DEST release and necessary ' - 'intermediate releases ' - '(default: current LTS release)') - parser.add_option('-s', '--source', metavar='SOURCE', - help='Backport from SOURCE release ' - '(default: current devel release)') - parser.add_option('-l', '--lpinstance', metavar='INSTANCE', default=None, - help='Launchpad instance to connect to ' - '(default: production).') - parser.add_option('--no-conf', action='store_true', - dest='no_conf', default=False, - help="Don't read config files or environment variables") + parser = optparse.OptionParser("%prog [options] package") + parser.add_option( + "-d", + "--destination", + metavar="DEST", + help="Backport to DEST release and necessary " + "intermediate releases " + "(default: current LTS release)", + ) + parser.add_option( + "-s", + "--source", + metavar="SOURCE", + help="Backport from SOURCE release (default: current devel release)", + ) + parser.add_option( + "-l", + "--lpinstance", + metavar="INSTANCE", + default=None, + help="Launchpad instance to connect to (default: production).", + ) + parser.add_option( + "--no-conf", + action="store_true", + dest="no_conf", + default=False, + help="Don't read config files or environment variables", + ) options, args = parser.parse_args() if len(args) != 1: @@ -280,15 +308,14 @@ def main(): config = UDTConfig(options.no_conf) if options.lpinstance is None: - options.lpinstance = config.get_value('LPINSTANCE') + options.lpinstance = config.get_value("LPINSTANCE") Launchpad.login(options.lpinstance) if options.source is None: - options.source = Distribution('ubuntu').getDevelopmentSeries().name + options.source = Distribution("ubuntu").getDevelopmentSeries().name try: - destinations = determine_destinations(options.source, - options.destination) + destinations = determine_destinations(options.source, options.destination) except DestinationException as e: Logger.error(str(e)) sys.exit(1) @@ -301,5 +328,5 @@ def main(): request_backport(package_spph, options.source, destinations) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/requestsync b/requestsync index 781834f..f8df736 100755 --- a/requestsync +++ b/requestsync @@ -39,6 +39,7 @@ from ubuntutools.question import confirmation_prompt, EditBugReport from ubuntutools.version import Version from ubuntutools import getLogger + Logger = getLogger() # @@ -48,46 +49,78 @@ Logger = getLogger() def main(): # Our usage options. - usage = ('Usage: %prog [options] ' - ' [ [base version]]') + usage = "Usage: %prog [options] [ [base version]]" parser = optparse.OptionParser(usage) - parser.add_option('-d', type='string', - dest='dist', default='unstable', - help='Debian distribution to sync from.') - parser.add_option('-k', type='string', - dest='keyid', default=None, - help='GnuPG key ID to use for signing report ' - '(only used when emailing the sync request).') - parser.add_option('-n', action='store_true', - dest='newpkg', default=False, - help='Whether package to sync is a new package in ' - 'Ubuntu.') - parser.add_option('--email', action='store_true', default=False, - help='Use a PGP-signed email for filing the sync ' - 'request, rather than the LP API.') - parser.add_option('--lp', dest='deprecated_lp_flag', - action='store_true', default=False, - help=optparse.SUPPRESS_HELP) - parser.add_option('-l', '--lpinstance', metavar='INSTANCE', - dest='lpinstance', default=None, - help='Launchpad instance to connect to ' - '(default: production).') - parser.add_option('-s', action='store_true', - dest='sponsorship', default=False, - help='Force sponsorship') - parser.add_option('-C', action='store_true', - dest='missing_changelog_ok', default=False, - help='Allow changelog to be manually filled in ' - 'when missing') - parser.add_option('-e', action='store_true', - dest='ffe', default=False, - help='Use this after FeatureFreeze for non-bug fix ' - 'syncs, changes default subscription to the ' - 'appropriate release team.') - parser.add_option('--no-conf', action='store_true', - dest='no_conf', default=False, - help="Don't read config files or environment variables") + parser.add_option( + "-d", + type="string", + dest="dist", + default="unstable", + help="Debian distribution to sync from.", + ) + parser.add_option( + "-k", + type="string", + dest="keyid", + default=None, + help="GnuPG key ID to use for signing report " + "(only used when emailing the sync request).", + ) + parser.add_option( + "-n", + action="store_true", + dest="newpkg", + default=False, + help="Whether package to sync is a new package in Ubuntu.", + ) + parser.add_option( + "--email", + action="store_true", + default=False, + help="Use a PGP-signed email for filing the sync request, rather than the LP API.", + ) + parser.add_option( + "--lp", + dest="deprecated_lp_flag", + action="store_true", + default=False, + help=optparse.SUPPRESS_HELP, + ) + parser.add_option( + "-l", + "--lpinstance", + metavar="INSTANCE", + dest="lpinstance", + default=None, + help="Launchpad instance to connect to (default: production).", + ) + parser.add_option( + "-s", action="store_true", dest="sponsorship", default=False, help="Force sponsorship" + ) + parser.add_option( + "-C", + action="store_true", + dest="missing_changelog_ok", + default=False, + help="Allow changelog to be manually filled in when missing", + ) + parser.add_option( + "-e", + action="store_true", + dest="ffe", + default=False, + help="Use this after FeatureFreeze for non-bug fix " + "syncs, changes default subscription to the " + "appropriate release team.", + ) + parser.add_option( + "--no-conf", + action="store_true", + dest="no_conf", + default=False, + help="Don't read config files or environment variables", + ) (options, args) = parser.parse_args() @@ -104,74 +137,78 @@ def main(): if options.email: options.lpapi = False else: - options.lpapi = config.get_value('USE_LPAPI', default=True, - boolean=True) + options.lpapi = config.get_value("USE_LPAPI", default=True, boolean=True) if options.lpinstance is None: - options.lpinstance = config.get_value('LPINSTANCE') + options.lpinstance = config.get_value("LPINSTANCE") if options.keyid is None: - options.keyid = config.get_value('KEYID') + options.keyid = config.get_value("KEYID") if not options.lpapi: - if options.lpinstance == 'production': - bug_mail_domain = 'bugs.launchpad.net' - elif options.lpinstance == 'staging': - bug_mail_domain = 'bugs.staging.launchpad.net' + if options.lpinstance == "production": + bug_mail_domain = "bugs.launchpad.net" + elif options.lpinstance == "staging": + bug_mail_domain = "bugs.staging.launchpad.net" else: - Logger.error('Error: Unknown launchpad instance: %s' - % options.lpinstance) + Logger.error("Error: Unknown launchpad instance: %s" % options.lpinstance) sys.exit(1) - mailserver_host = config.get_value('SMTP_SERVER', - default=None, - compat_keys=['UBUSMTP', 'DEBSMTP']) + mailserver_host = config.get_value( + "SMTP_SERVER", default=None, compat_keys=["UBUSMTP", "DEBSMTP"] + ) if not options.lpapi and not mailserver_host: try: import DNS + DNS.DiscoverNameServers() mxlist = DNS.mxlookup(bug_mail_domain) firstmx = mxlist[0] mailserver_host = firstmx[1] except ImportError: - Logger.error('Please install python-dns to support ' - 'Launchpad mail server lookup.') + Logger.error("Please install python-dns to support Launchpad mail server lookup.") sys.exit(1) - mailserver_port = config.get_value('SMTP_PORT', default=25, - compat_keys=['UBUSMTP_PORT', - 'DEBSMTP_PORT']) - mailserver_user = config.get_value('SMTP_USER', - compat_keys=['UBUSMTP_USER', - 'DEBSMTP_USER']) - mailserver_pass = config.get_value('SMTP_PASS', - compat_keys=['UBUSMTP_PASS', - 'DEBSMTP_PASS']) + mailserver_port = config.get_value( + "SMTP_PORT", default=25, compat_keys=["UBUSMTP_PORT", "DEBSMTP_PORT"] + ) + mailserver_user = config.get_value("SMTP_USER", compat_keys=["UBUSMTP_USER", "DEBSMTP_USER"]) + mailserver_pass = config.get_value("SMTP_PASS", compat_keys=["UBUSMTP_PASS", "DEBSMTP_PASS"]) # import the needed requestsync module if options.lpapi: - from ubuntutools.requestsync.lp import (check_existing_reports, - get_debian_srcpkg, - get_ubuntu_srcpkg, - get_ubuntu_delta_changelog, - need_sponsorship, post_bug) + from ubuntutools.requestsync.lp import ( + check_existing_reports, + get_debian_srcpkg, + get_ubuntu_srcpkg, + get_ubuntu_delta_changelog, + need_sponsorship, + post_bug, + ) from ubuntutools.lp.lpapicache import Distribution, Launchpad + # See if we have LP credentials and exit if we don't - # cannot continue in this case try: # devel for changelogUrl() - Launchpad.login(service=options.lpinstance, api_version='devel') + Launchpad.login(service=options.lpinstance, api_version="devel") except IOError: sys.exit(1) else: - from ubuntutools.requestsync.mail import (check_existing_reports, - get_debian_srcpkg, - get_ubuntu_srcpkg, - get_ubuntu_delta_changelog, - mail_bug, need_sponsorship) - if not any(x in os.environ for x in ('UBUMAIL', 'DEBEMAIL', 'EMAIL')): - Logger.error('The environment variable UBUMAIL, DEBEMAIL or EMAIL needs ' - 'to be set to let this script mail the sync request.') + from ubuntutools.requestsync.mail import ( + check_existing_reports, + get_debian_srcpkg, + get_ubuntu_srcpkg, + get_ubuntu_delta_changelog, + mail_bug, + need_sponsorship, + ) + + if not any(x in os.environ for x in ("UBUMAIL", "DEBEMAIL", "EMAIL")): + Logger.error( + "The environment variable UBUMAIL, DEBEMAIL or EMAIL needs " + "to be set to let this script mail the sync request." + ) sys.exit(1) newsource = options.newpkg @@ -185,30 +222,30 @@ def main(): if len(args) == 1: if lpapi: - release = Distribution('ubuntu').getDevelopmentSeries().name + release = Distribution("ubuntu").getDevelopmentSeries().name else: ubu_info = UbuntuDistroInfo() release = ubu_info.devel() - Logger.warning('Target release missing - assuming %s' % release) + Logger.warning("Target release missing - assuming %s" % release) elif len(args) == 2: release = args[1] elif len(args) == 3: release = args[1] force_base_version = Version(args[2]) else: - Logger.error('Too many arguments.') + Logger.error("Too many arguments.") parser.print_help() sys.exit(1) # Get the current Ubuntu source package try: - ubuntu_srcpkg = get_ubuntu_srcpkg(srcpkg, release, 'Proposed') + ubuntu_srcpkg = get_ubuntu_srcpkg(srcpkg, release, "Proposed") ubuntu_version = Version(ubuntu_srcpkg.getVersion()) ubuntu_component = ubuntu_srcpkg.getComponent() newsource = False # override the -n flag except udtexceptions.PackageNotFoundException: ubuntu_srcpkg = None - ubuntu_version = Version('~') + ubuntu_version = Version("~") ubuntu_component = None # Set after getting the Debian info if not newsource: Logger.info("'%s' doesn't exist in 'Ubuntu %s'." % (srcpkg, release)) @@ -232,15 +269,16 @@ def main(): sys.exit(1) if ubuntu_component is None: - if debian_component == 'main': - ubuntu_component = 'universe' + if debian_component == "main": + ubuntu_component = "universe" else: - ubuntu_component = 'multiverse' + ubuntu_component = "multiverse" # Stop if Ubuntu has already the version from Debian or a newer version if (ubuntu_version >= debian_version) and options.lpapi: # try rmadison import ubuntutools.requestsync.mail + try: debian_srcpkg = ubuntutools.requestsync.mail.get_debian_srcpkg(srcpkg, distro) debian_version = Version(debian_srcpkg.getVersion()) @@ -250,13 +288,16 @@ def main(): sys.exit(1) if ubuntu_version == debian_version: - Logger.error('The versions in Debian and Ubuntu are the ' - 'same already (%s). Aborting.' % ubuntu_version) + Logger.error( + "The versions in Debian and Ubuntu are the " + "same already (%s). Aborting." % ubuntu_version + ) sys.exit(1) if ubuntu_version > debian_version: - Logger.error('The version in Ubuntu (%s) is newer than ' - 'the version in Debian (%s). Aborting.' - % (ubuntu_version, debian_version)) + Logger.error( + "The version in Ubuntu (%s) is newer than " + "the version in Debian (%s). Aborting." % (ubuntu_version, debian_version) + ) sys.exit(1) # -s flag not specified - check if we do need sponsorship @@ -264,40 +305,47 @@ def main(): sponsorship = need_sponsorship(srcpkg, ubuntu_component, release) if not sponsorship and not ffe: - Logger.error('Consider using syncpackage(1) for syncs that ' - 'do not require feature freeze exceptions.') + Logger.error( + "Consider using syncpackage(1) for syncs that " + "do not require feature freeze exceptions." + ) # Check for existing package reports if not newsource: check_existing_reports(srcpkg) # Generate bug report - pkg_to_sync = ('%s %s (%s) from Debian %s (%s)' - % (srcpkg, debian_version, ubuntu_component, - distro, debian_component)) + pkg_to_sync = "%s %s (%s) from Debian %s (%s)" % ( + srcpkg, + debian_version, + ubuntu_component, + distro, + debian_component, + ) title = "Sync %s" % pkg_to_sync if ffe: title = "FFe: " + title report = "Please sync %s\n\n" % pkg_to_sync - if 'ubuntu' in str(ubuntu_version): + if "ubuntu" in str(ubuntu_version): need_interaction = True - Logger.info('Changes have been made to the package in Ubuntu.') - Logger.info('Please edit the report and give an explanation.') - Logger.info('Not saving the report file will abort the request.') - report += ('Explanation of the Ubuntu delta and why it can be ' - 'dropped:\n%s\n>>> ENTER_EXPLANATION_HERE <<<\n\n' - % get_ubuntu_delta_changelog(ubuntu_srcpkg)) + Logger.info("Changes have been made to the package in Ubuntu.") + Logger.info("Please edit the report and give an explanation.") + Logger.info("Not saving the report file will abort the request.") + report += ( + "Explanation of the Ubuntu delta and why it can be " + "dropped:\n%s\n>>> ENTER_EXPLANATION_HERE <<<\n\n" + % get_ubuntu_delta_changelog(ubuntu_srcpkg) + ) if ffe: need_interaction = True - Logger.info('To approve FeatureFreeze exception, you need to state') - Logger.info('the reason why you feel it is necessary.') - Logger.info('Not saving the report file will abort the request.') - report += ('Explanation of FeatureFreeze exception:\n' - '>>> ENTER_EXPLANATION_HERE <<<\n\n') + Logger.info("To approve FeatureFreeze exception, you need to state") + Logger.info("the reason why you feel it is necessary.") + Logger.info("Not saving the report file will abort the request.") + report += "Explanation of FeatureFreeze exception:\n>>> ENTER_EXPLANATION_HERE <<<\n\n" if need_interaction: confirmation_prompt() @@ -305,17 +353,18 @@ def main(): base_version = force_base_version or ubuntu_version if newsource: - report += 'All changelog entries:\n\n' + report += "All changelog entries:\n\n" else: - report += ('Changelog entries since current %s version %s:\n\n' - % (release, ubuntu_version)) + report += "Changelog entries since current %s version %s:\n\n" % (release, ubuntu_version) changelog = debian_srcpkg.getChangelog(since_version=base_version) if not changelog: if not options.missing_changelog_ok: - Logger.error("Did not retrieve any changelog entries. " - "Do you need to specify '-C'? " - "Was the package recently uploaded? (check " - "http://packages.debian.org/changelogs/)") + Logger.error( + "Did not retrieve any changelog entries. " + "Do you need to specify '-C'? " + "Was the package recently uploaded? (check " + "http://packages.debian.org/changelogs/)" + ) sys.exit(1) else: need_interaction = True @@ -326,36 +375,49 @@ def main(): editor.edit(optional=not need_interaction) title, report = editor.get_report() - if 'XXX FIXME' in report: - Logger.error("changelog boilerplate found in report, " - "please manually add changelog when using '-C'") + if "XXX FIXME" in report: + Logger.error( + "changelog boilerplate found in report, " + "please manually add changelog when using '-C'" + ) sys.exit(1) # bug status and bug subscriber - status = 'confirmed' - subscribe = 'ubuntu-archive' + status = "confirmed" + subscribe = "ubuntu-archive" if sponsorship: - status = 'new' - subscribe = 'ubuntu-sponsors' + status = "new" + subscribe = "ubuntu-sponsors" if ffe: - status = 'new' - subscribe = 'ubuntu-release' + status = "new" + subscribe = "ubuntu-release" srcpkg = not newsource and srcpkg or None if lpapi: # Map status to the values expected by LP API - mapping = {'new': 'New', 'confirmed': 'Confirmed'} + mapping = {"new": "New", "confirmed": "Confirmed"} # Post sync request using LP API post_bug(srcpkg, subscribe, mapping[status], title, report) else: email_from = ubu_email(export=False)[1] # Mail sync request - mail_bug(srcpkg, subscribe, status, title, report, bug_mail_domain, - options.keyid, email_from, mailserver_host, mailserver_port, - mailserver_user, mailserver_pass) + mail_bug( + srcpkg, + subscribe, + status, + title, + report, + bug_mail_domain, + options.keyid, + email_from, + mailserver_host, + mailserver_port, + mailserver_user, + mailserver_pass, + ) -if __name__ == '__main__': +if __name__ == "__main__": try: main() except KeyboardInterrupt: diff --git a/reverse-depends b/reverse-depends index 15aa669..ac7bcf0 100755 --- a/reverse-depends +++ b/reverse-depends @@ -19,11 +19,11 @@ import sys from distro_info import DistroDataOutdated -from ubuntutools.misc import (system_distribution, vendor_to_distroinfo, - codename_to_distribution) +from ubuntutools.misc import system_distribution, vendor_to_distroinfo, codename_to_distribution from ubuntutools.rdepends import query_rdepends, RDependsException from ubuntutools import getLogger + Logger = getLogger() DEFAULT_MAX_DEPTH = 10 # We want avoid any infinite loop... @@ -35,77 +35,107 @@ def main(): default_release = system_distro_info.devel() except DistroDataOutdated as e: Logger.warning(e) - default_release = 'unstable' + default_release = "unstable" - description = ("List reverse-dependencies of package. " - "If the package name is prefixed with src: then the " - "reverse-dependencies of all the binary packages that " - "the specified source package builds will be listed.") + description = ( + "List reverse-dependencies of package. " + "If the package name is prefixed with src: then the " + "reverse-dependencies of all the binary packages that " + "the specified source package builds will be listed." + ) parser = argparse.ArgumentParser(description=description) - parser.add_argument('-r', '--release', default=default_release, - help='Query dependencies in RELEASE. ' - 'Default: %s' % default_release) - parser.add_argument('-R', '--without-recommends', action='store_false', - dest='recommends', - help='Only consider Depends relationships, ' - 'not Recommends') - parser.add_argument('-s', '--with-suggests', action='store_true', - help='Also consider Suggests relationships') - parser.add_argument('-b', '--build-depends', action='store_true', - help='Query build dependencies (synonym for --arch=source)') - parser.add_argument('-a', '--arch', default='any', - help='Query dependencies in ARCH. Default: any') - parser.add_argument('-c', '--component', action='append', - help='Only consider reverse-dependencies in COMPONENT. ' - 'Can be specified multiple times. Default: all') - parser.add_argument('-l', '--list', action='store_true', - help='Display a simple, machine-readable list') - parser.add_argument('-u', '--service-url', metavar='URL', - dest='server', default=None, - help='Reverse Dependencies webservice URL. ' - 'Default: UbuntuWire') - parser.add_argument('-x', '--recursive', action='store_true', - help='Consider to find reverse dependencies recursively.') - parser.add_argument('-d', '--recursive-depth', type=int, - default=DEFAULT_MAX_DEPTH, - help='If recusive, you can specify the depth.') - parser.add_argument('package') + parser.add_argument( + "-r", + "--release", + default=default_release, + help="Query dependencies in RELEASE. Default: %s" % default_release, + ) + parser.add_argument( + "-R", + "--without-recommends", + action="store_false", + dest="recommends", + help="Only consider Depends relationships, not Recommends", + ) + parser.add_argument( + "-s", "--with-suggests", action="store_true", help="Also consider Suggests relationships" + ) + parser.add_argument( + "-b", + "--build-depends", + action="store_true", + help="Query build dependencies (synonym for --arch=source)", + ) + parser.add_argument( + "-a", "--arch", default="any", help="Query dependencies in ARCH. Default: any" + ) + parser.add_argument( + "-c", + "--component", + action="append", + help="Only consider reverse-dependencies in COMPONENT. " + "Can be specified multiple times. Default: all", + ) + parser.add_argument( + "-l", "--list", action="store_true", help="Display a simple, machine-readable list" + ) + parser.add_argument( + "-u", + "--service-url", + metavar="URL", + dest="server", + default=None, + help="Reverse Dependencies webservice URL. Default: UbuntuWire", + ) + parser.add_argument( + "-x", + "--recursive", + action="store_true", + help="Consider to find reverse dependencies recursively.", + ) + parser.add_argument( + "-d", + "--recursive-depth", + type=int, + default=DEFAULT_MAX_DEPTH, + help="If recusive, you can specify the depth.", + ) + parser.add_argument("package") options = parser.parse_args() opts = {} if options.server is not None: - opts['server'] = options.server + opts["server"] = options.server # Convert unstable/testing aliases to codenames: distribution = codename_to_distribution(options.release) if not distribution: - parser.error('Unknown release codename %s' % options.release) + parser.error("Unknown release codename %s" % options.release) distro_info = vendor_to_distroinfo(distribution)() try: - options.release = distro_info.codename(options.release, - default=options.release) + options.release = distro_info.codename(options.release, default=options.release) except DistroDataOutdated: # We already logged a warning pass if options.build_depends: - options.arch = 'source' + options.arch = "source" - if options.arch == 'source': + if options.arch == "source": fields = [ - 'Reverse-Build-Depends', - 'Reverse-Build-Depends-Indep', - 'Reverse-Build-Depends-Arch', - 'Reverse-Testsuite-Triggers', + "Reverse-Build-Depends", + "Reverse-Build-Depends-Indep", + "Reverse-Build-Depends-Arch", + "Reverse-Testsuite-Triggers", ] else: - fields = ['Reverse-Depends'] + fields = ["Reverse-Depends"] if options.recommends: - fields.append('Reverse-Recommends') + fields.append("Reverse-Recommends") if options.with_suggests: - fields.append('Reverse-Suggests') + fields.append("Reverse-Suggests") def build_results(package, result, fields, component, recursive): try: @@ -119,9 +149,9 @@ def main(): if fields: data = {k: v for k, v in data.items() if k in fields} if component: - data = {k: [rdep for rdep in v - if rdep['Component'] in component] - for k, v in data.items()} + data = { + k: [rdep for rdep in v if rdep["Component"] in component] for k, v in data.items() + } data = {k: v for k, v in data.items() if v} result[package] = data @@ -129,13 +159,16 @@ def main(): if recursive > 0: for rdeps in result[package].values(): for rdep in rdeps: - build_results( - rdep['Package'], result, fields, component, recursive - 1) + build_results(rdep["Package"], result, fields, component, recursive - 1) result = {} build_results( - options.package, result, fields, options.component, - options.recursive and options.recursive_depth or 0) + options.package, + result, + fields, + options.component, + options.recursive and options.recursive_depth or 0, + ) if options.list: display_consise(result) @@ -150,50 +183,55 @@ def display_verbose(package, values): def log_field(field): Logger.info(field) - Logger.info('=' * len(field)) + Logger.info("=" * len(field)) def log_package(values, package, arch, dependency, offset=0): - line = ' ' * offset + '* %s' % package + line = " " * offset + "* %s" % package if all_archs and set(arch) != all_archs: - line += ' [%s]' % ' '.join(sorted(arch)) + line += " [%s]" % " ".join(sorted(arch)) if dependency: if len(line) < 30: - line += ' ' * (30 - len(line)) - line += ' (for %s)' % dependency + line += " " * (30 - len(line)) + line += " (for %s)" % dependency Logger.info(line) data = values.get(package) if data: offset = offset + 1 for rdeps in data.values(): for rdep in rdeps: - log_package(values, - rdep['Package'], - rdep.get('Architectures', all_archs), - rdep.get('Dependency'), - offset) + log_package( + values, + rdep["Package"], + rdep.get("Architectures", all_archs), + rdep.get("Dependency"), + offset, + ) all_archs = set() # This isn't accurate, but we make up for it by displaying what we found for data in values.values(): for rdeps in data.values(): for rdep in rdeps: - if 'Architectures' in rdep: - all_archs.update(rdep['Architectures']) + if "Architectures" in rdep: + all_archs.update(rdep["Architectures"]) for field, rdeps in values[package].items(): Logger.info(field) - rdeps.sort(key=lambda x: x['Package']) + rdeps.sort(key=lambda x: x["Package"]) for rdep in rdeps: - log_package(values, - rdep['Package'], - rdep.get('Architectures', all_archs), - rdep.get('Dependency')) + log_package( + values, + rdep["Package"], + rdep.get("Architectures", all_archs), + rdep.get("Dependency"), + ) Logger.info("") if all_archs: - Logger.info("Packages without architectures listed are " - "reverse-dependencies in: %s" - % ', '.join(sorted(list(all_archs)))) + Logger.info( + "Packages without architectures listed are " + "reverse-dependencies in: %s" % ", ".join(sorted(list(all_archs))) + ) def display_consise(values): @@ -201,10 +239,10 @@ def display_consise(values): for data in values.values(): for rdeps in data.values(): for rdep in rdeps: - result.add(rdep['Package']) + result.add(rdep["Package"]) - Logger.info('\n'.join(sorted(list(result)))) + Logger.info("\n".join(sorted(list(result)))) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/run-linters b/run-linters index e9bfc9d..9d7039a 100755 --- a/run-linters +++ b/run-linters @@ -7,4 +7,4 @@ set -eu PYTHON_SCRIPTS=$(grep -l -r '^#! */usr/bin/python3$' .) echo "Running flake8..." -flake8 --max-line-length=99 . $PYTHON_SCRIPTS +flake8 --max-line-length=99 --ignore=E203,W503 . $PYTHON_SCRIPTS diff --git a/seeded-in-ubuntu b/seeded-in-ubuntu index b3f9d7f..c395941 100755 --- a/seeded-in-ubuntu +++ b/seeded-in-ubuntu @@ -22,43 +22,42 @@ import os import time import urllib.request -from ubuntutools.lp.lpapicache import (Distribution, Launchpad, - PackageNotFoundException) +from ubuntutools.lp.lpapicache import Distribution, Launchpad, PackageNotFoundException from ubuntutools import getLogger + Logger = getLogger() -DATA_URL = 'http://qa.ubuntuwire.org/ubuntu-seeded-packages/seeded.json.gz' +DATA_URL = "http://qa.ubuntuwire.org/ubuntu-seeded-packages/seeded.json.gz" def load_index(url): - '''Download a new copy of the image contents index, if necessary, + """Download a new copy of the image contents index, if necessary, and read it. - ''' - cachedir = os.path.expanduser('~/.cache/ubuntu-dev-tools') - fn = os.path.join(cachedir, 'seeded.json.gz') + """ + cachedir = os.path.expanduser("~/.cache/ubuntu-dev-tools") + fn = os.path.join(cachedir, "seeded.json.gz") - if (not os.path.isfile(fn) - or time.time() - os.path.getmtime(fn) > 60 * 60 * 2): + if not os.path.isfile(fn) or time.time() - os.path.getmtime(fn) > 60 * 60 * 2: if not os.path.isdir(cachedir): os.makedirs(cachedir) urllib.request.urlretrieve(url, fn) try: - with gzip.open(fn, 'r') as f: + with gzip.open(fn, "r") as f: return json.load(f) except Exception as e: - Logger.error("Unable to parse seed data: %s. " - "Deleting cached data, please try again.", - str(e)) + Logger.error( + "Unable to parse seed data: %s. Deleting cached data, please try again.", str(e) + ) os.unlink(fn) def resolve_binaries(sources): - '''Return a dict of source:binaries for all binary packages built by + """Return a dict of source:binaries for all binary packages built by sources - ''' - archive = Distribution('ubuntu').getArchive() + """ + archive = Distribution("ubuntu").getArchive() binaries = {} for source in sources: try: @@ -66,28 +65,26 @@ def resolve_binaries(sources): except PackageNotFoundException as e: Logger.error(str(e)) continue - binaries[source] = sorted(set(bpph.getPackageName() - for bpph in spph.getBinaries())) + binaries[source] = sorted(set(bpph.getPackageName() for bpph in spph.getBinaries())) return binaries def present_on(appearences): - '''Format a list of (flavor, type) tuples into a human-readable string''' + """Format a list of (flavor, type) tuples into a human-readable string""" present = collections.defaultdict(set) for flavor, type_ in appearences: present[flavor].add(type_) for flavor, types in present.items(): if len(types) > 1: - types.discard('supported') - output = [' %s: %s' % (flavor, ', '.join(sorted(types))) - for flavor, types in present.items()] + types.discard("supported") + output = [" %s: %s" % (flavor, ", ".join(sorted(types))) for flavor, types in present.items()] output.sort() - return '\n'.join(output) + return "\n".join(output) def output_binaries(index, binaries): - '''Print binaries found in index''' + """Print binaries found in index""" for binary in binaries: if binary in index: Logger.info("%s is seeded in:" % binary) @@ -97,13 +94,14 @@ def output_binaries(index, binaries): def output_by_source(index, by_source): - '''Logger.Info(binaries found in index. Grouped by source''' + """Logger.Info(binaries found in index. Grouped by source""" for source, binaries in by_source.items(): seen = False if not binaries: - Logger.info("Status unknown: No binary packages built by the latest " - "%s.\nTry again using -b and the expected binary packages." - % source) + Logger.info( + "Status unknown: No binary packages built by the latest " + "%s.\nTry again using -b and the expected binary packages." % source + ) continue for binary in binaries: if binary in index: @@ -115,16 +113,22 @@ def output_by_source(index, by_source): def main(): - '''Query which images the specified packages are on''' - parser = optparse.OptionParser('%prog [options] package...') - parser.add_option('-b', '--binary', - default=False, action='store_true', - help="Binary packages are being specified, " - "not source packages (fast)") - parser.add_option('-u', '--data-url', metavar='URL', - default=DATA_URL, - help='URL for the seeded packages index. ' - 'Default: UbuntuWire') + """Query which images the specified packages are on""" + parser = optparse.OptionParser("%prog [options] package...") + parser.add_option( + "-b", + "--binary", + default=False, + action="store_true", + help="Binary packages are being specified, not source packages (fast)", + ) + parser.add_option( + "-u", + "--data-url", + metavar="URL", + default=DATA_URL, + help="URL for the seeded packages index. Default: UbuntuWire", + ) options, args = parser.parse_args() if len(args) < 1: @@ -141,5 +145,5 @@ def main(): output_by_source(index, binaries) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/setup.py b/setup.py index 55ba671..51dc0b2 100755 --- a/setup.py +++ b/setup.py @@ -21,74 +21,74 @@ def make_pep440_compliant(version: str) -> str: # look/set what version we have changelog = "debian/changelog" if os.path.exists(changelog): - head = open(changelog, 'r', encoding='utf-8').readline() + head = open(changelog, "r", encoding="utf-8").readline() match = re.compile(r".*\((.*)\).*").match(head) if match: version = match.group(1) scripts = [ - 'backportpackage', - 'bitesize', - 'check-mir', - 'check-symbols', - 'dch-repeat', - 'grab-merge', - 'grep-merges', - 'import-bug-from-debian', - 'merge-changelog', - 'mk-sbuild', - 'pbuilder-dist', - 'pbuilder-dist-simple', - 'pull-pkg', - 'pull-debian-debdiff', - 'pull-debian-source', - 'pull-debian-debs', - 'pull-debian-ddebs', - 'pull-debian-udebs', - 'pull-lp-source', - 'pull-lp-debs', - 'pull-lp-ddebs', - 'pull-lp-udebs', - 'pull-ppa-source', - 'pull-ppa-debs', - 'pull-ppa-ddebs', - 'pull-ppa-udebs', - 'pull-uca-source', - 'pull-uca-debs', - 'pull-uca-ddebs', - 'pull-uca-udebs', - 'requestbackport', - 'requestsync', - 'reverse-depends', - 'seeded-in-ubuntu', - 'setup-packaging-environment', - 'sponsor-patch', - 'submittodebian', - 'syncpackage', - 'ubuntu-build', - 'ubuntu-iso', - 'ubuntu-upload-permission', - 'update-maintainer', + "backportpackage", + "bitesize", + "check-mir", + "check-symbols", + "dch-repeat", + "grab-merge", + "grep-merges", + "import-bug-from-debian", + "merge-changelog", + "mk-sbuild", + "pbuilder-dist", + "pbuilder-dist-simple", + "pull-pkg", + "pull-debian-debdiff", + "pull-debian-source", + "pull-debian-debs", + "pull-debian-ddebs", + "pull-debian-udebs", + "pull-lp-source", + "pull-lp-debs", + "pull-lp-ddebs", + "pull-lp-udebs", + "pull-ppa-source", + "pull-ppa-debs", + "pull-ppa-ddebs", + "pull-ppa-udebs", + "pull-uca-source", + "pull-uca-debs", + "pull-uca-ddebs", + "pull-uca-udebs", + "requestbackport", + "requestsync", + "reverse-depends", + "seeded-in-ubuntu", + "setup-packaging-environment", + "sponsor-patch", + "submittodebian", + "syncpackage", + "ubuntu-build", + "ubuntu-iso", + "ubuntu-upload-permission", + "update-maintainer", ] data_files = [ - ('share/bash-completion/completions', glob.glob("bash_completion/*")), - ('share/man/man1', glob.glob("doc/*.1")), - ('share/man/man5', glob.glob("doc/*.5")), - ('share/ubuntu-dev-tools', ['enforced-editing-wrapper']), + ("share/bash-completion/completions", glob.glob("bash_completion/*")), + ("share/man/man1", glob.glob("doc/*.1")), + ("share/man/man5", glob.glob("doc/*.5")), + ("share/ubuntu-dev-tools", ["enforced-editing-wrapper"]), ] -if __name__ == '__main__': +if __name__ == "__main__": setup( - name='ubuntu-dev-tools', + name="ubuntu-dev-tools", version=make_pep440_compliant(version), scripts=scripts, packages=[ - 'ubuntutools', - 'ubuntutools/lp', - 'ubuntutools/requestsync', - 'ubuntutools/sponsor_patch', - 'ubuntutools/test', + "ubuntutools", + "ubuntutools/lp", + "ubuntutools/requestsync", + "ubuntutools/sponsor_patch", + "ubuntutools/test", ], data_files=data_files, - test_suite='ubuntutools.test', + test_suite="ubuntutools.test", ) diff --git a/sponsor-patch b/sponsor-patch index 4125a44..3a8f886 100755 --- a/sponsor-patch +++ b/sponsor-patch @@ -26,45 +26,103 @@ from ubuntutools.config import UDTConfig from ubuntutools.sponsor_patch.sponsor_patch import sponsor_patch, check_dependencies from ubuntutools import getLogger + Logger = getLogger() def parse(script_name): """Parse the command line parameters.""" - usage = ("%s [options] \n" % (script_name) - + "One of --upload, --workdir, or --sponsor must be specified.") + usage = ( + "%s [options] \n" % (script_name) + + "One of --upload, --workdir, or --sponsor must be specified." + ) epilog = "See %s(1) for more info." % (script_name) parser = optparse.OptionParser(usage=usage, epilog=epilog) - parser.add_option("-b", "--build", dest="build", - help="Build the package with the specified builder.", - action="store_true", default=False) - parser.add_option("-B", "--builder", dest="builder", default=None, - help="Specify the package builder (default pbuilder)") - parser.add_option("-e", "--edit", - help="launch sub-shell to allow editing of the patch", - dest="edit", action="store_true", default=False) - parser.add_option("-k", "--key", dest="keyid", default=None, - help="Specify the key ID to be used for signing.") - parser.add_option("-l", "--lpinstance", dest="lpinstance", default=None, - help="Launchpad instance to connect to " - "(default: production)", - metavar="INSTANCE") - parser.add_option("--no-conf", dest="no_conf", default=False, - help="Don't read config files or environment variables.", - action="store_true") - parser.add_option("-s", "--sponsor", help="sponsoring; equals -b -u ubuntu", - dest="sponsoring", action="store_true", default=False) - parser.add_option("-u", "--upload", dest="upload", default=None, - help="Specify an upload destination (default none).") - parser.add_option("-U", "--update", dest="update", default=False, - action="store_true", - help="Update the build environment before building.") - parser.add_option("-v", "--verbose", help="print more information", - dest="verbose", action="store_true", default=False) - parser.add_option("-w", "--workdir", dest="workdir", default=None, - help="Specify a working directory (default is a " - "temporary directory, deleted afterwards).") + parser.add_option( + "-b", + "--build", + dest="build", + help="Build the package with the specified builder.", + action="store_true", + default=False, + ) + parser.add_option( + "-B", + "--builder", + dest="builder", + default=None, + help="Specify the package builder (default pbuilder)", + ) + parser.add_option( + "-e", + "--edit", + help="launch sub-shell to allow editing of the patch", + dest="edit", + action="store_true", + default=False, + ) + parser.add_option( + "-k", + "--key", + dest="keyid", + default=None, + help="Specify the key ID to be used for signing.", + ) + parser.add_option( + "-l", + "--lpinstance", + dest="lpinstance", + default=None, + help="Launchpad instance to connect to (default: production)", + metavar="INSTANCE", + ) + parser.add_option( + "--no-conf", + dest="no_conf", + default=False, + help="Don't read config files or environment variables.", + action="store_true", + ) + parser.add_option( + "-s", + "--sponsor", + help="sponsoring; equals -b -u ubuntu", + dest="sponsoring", + action="store_true", + default=False, + ) + parser.add_option( + "-u", + "--upload", + dest="upload", + default=None, + help="Specify an upload destination (default none).", + ) + parser.add_option( + "-U", + "--update", + dest="update", + default=False, + action="store_true", + help="Update the build environment before building.", + ) + parser.add_option( + "-v", + "--verbose", + help="print more information", + dest="verbose", + action="store_true", + default=False, + ) + parser.add_option( + "-w", + "--workdir", + dest="workdir", + default=None, + help="Specify a working directory (default is a " + "temporary directory, deleted afterwards).", + ) (options, args) = parser.parse_args() if options.verbose: @@ -113,19 +171,26 @@ def main(): sys.exit(1) if not options.upload and not options.workdir: - Logger.error("Please specify either a working directory or an upload " - "target!") + Logger.error("Please specify either a working directory or an upload target!") sys.exit(1) if options.workdir is None: - workdir = tempfile.mkdtemp(prefix=script_name+"-") + workdir = tempfile.mkdtemp(prefix=script_name + "-") else: workdir = options.workdir try: - sponsor_patch(bug_number, options.build, builder, options.edit, - options.keyid, options.lpinstance, options.update, - options.upload, workdir) + sponsor_patch( + bug_number, + options.build, + builder, + options.edit, + options.keyid, + options.lpinstance, + options.update, + options.upload, + workdir, + ) except KeyboardInterrupt: Logger.error("User abort.") sys.exit(2) diff --git a/submittodebian b/submittodebian index a2cc145..e78e2c4 100755 --- a/submittodebian +++ b/submittodebian @@ -40,13 +40,14 @@ from ubuntutools.question import YesNoQuestion, EditFile from ubuntutools.update_maintainer import update_maintainer, restore_maintainer from ubuntutools import getLogger + Logger = getLogger() def get_most_recent_debian_version(changelog): for block in changelog: version = block.version.full_version - if not re.search('(ubuntu|build)', version): + if not re.search("(ubuntu|build)", version): return version @@ -65,19 +66,20 @@ In Ubuntu, the attached patch was applied to achieve the following: %s Thanks for considering the patch. -""" % ("\n".join([a for a in entry.changes()])) +""" % ( + "\n".join([a for a in entry.changes()]) + ) return msg def build_source_package(): - if os.path.isdir('.bzr'): - cmd = ['bzr', 'bd', '--builder=dpkg-buildpackage', '-S', - '--', '-uc', '-us', '-nc'] + if os.path.isdir(".bzr"): + cmd = ["bzr", "bd", "--builder=dpkg-buildpackage", "-S", "--", "-uc", "-us", "-nc"] else: - cmd = ['dpkg-buildpackage', '-S', '-uc', '-us', '-nc'] + cmd = ["dpkg-buildpackage", "-S", "-uc", "-us", "-nc"] env = os.environ.copy() # Unset DEBEMAIL in case there's an @ubuntu.com e-mail address - env.pop('DEBEMAIL', None) + env.pop("DEBEMAIL", None) check_call(cmd, env=env) @@ -88,30 +90,34 @@ def gen_debdiff(tmpdir, changelog): newver = next(changelog_it).version oldver = next(changelog_it).version - debdiff = os.path.join(tmpdir, '%s_%s.debdiff' % (pkg, newver)) + debdiff = os.path.join(tmpdir, "%s_%s.debdiff" % (pkg, newver)) - diff_cmd = ['bzr', 'diff', '-r', 'tag:' + str(oldver)] + diff_cmd = ["bzr", "diff", "-r", "tag:" + str(oldver)] if call(diff_cmd, stdout=DEVNULL, stderr=DEVNULL) == 1: Logger.info("Extracting bzr diff between %s and %s" % (oldver, newver)) else: if oldver.epoch is not None: - oldver = str(oldver)[str(oldver).index(":") + 1:] + oldver = str(oldver)[str(oldver).index(":") + 1 :] if newver.epoch is not None: - newver = str(newver)[str(newver).index(":") + 1:] + newver = str(newver)[str(newver).index(":") + 1 :] - olddsc = '../%s_%s.dsc' % (pkg, oldver) - newdsc = '../%s_%s.dsc' % (pkg, newver) + olddsc = "../%s_%s.dsc" % (pkg, oldver) + newdsc = "../%s_%s.dsc" % (pkg, newver) check_file(olddsc) check_file(newdsc) Logger.info("Generating debdiff between %s and %s" % (oldver, newver)) - diff_cmd = ['debdiff', olddsc, newdsc] + diff_cmd = ["debdiff", olddsc, newdsc] - with Popen(diff_cmd, stdout=PIPE, encoding='utf-8') as diff: - with open(debdiff, 'w', encoding='utf-8') as debdiff_f: - run(['filterdiff', '-x', '*changelog*'], - stdin=diff.stdout, stdout=debdiff_f, encoding='utf-8') + with Popen(diff_cmd, stdout=PIPE, encoding="utf-8") as diff: + with open(debdiff, "w", encoding="utf-8") as debdiff_f: + run( + ["filterdiff", "-x", "*changelog*"], + stdin=diff.stdout, + stdout=debdiff_f, + encoding="utf-8", + ) return debdiff @@ -131,57 +137,65 @@ def submit_bugreport(body, debdiff, deb_version, changelog): devel = UbuntuDistroInfo().devel() except DistroDataOutdated as e: Logger.info(str(e)) - devel = '' + devel = "" - if os.path.dirname(sys.argv[0]).startswith('/usr/bin'): - editor_path = '/usr/share/ubuntu-dev-tools' + if os.path.dirname(sys.argv[0]).startswith("/usr/bin"): + editor_path = "/usr/share/ubuntu-dev-tools" else: editor_path = os.path.dirname(sys.argv[0]) env = dict(os.environ.items()) - if 'EDITOR' in env: - env['UDT_EDIT_WRAPPER_EDITOR'] = env['EDITOR'] - if 'VISUAL' in env: - env['UDT_EDIT_WRAPPER_VISUAL'] = env['VISUAL'] - env['EDITOR'] = os.path.join(editor_path, 'enforced-editing-wrapper') - env['VISUAL'] = os.path.join(editor_path, 'enforced-editing-wrapper') - env['UDT_EDIT_WRAPPER_TEMPLATE_RE'] = ( - '.*REPLACE THIS WITH ACTUAL INFORMATION.*') - env['UDT_EDIT_WRAPPER_FILE_DESCRIPTION'] = 'bug report' + if "EDITOR" in env: + env["UDT_EDIT_WRAPPER_EDITOR"] = env["EDITOR"] + if "VISUAL" in env: + env["UDT_EDIT_WRAPPER_VISUAL"] = env["VISUAL"] + env["EDITOR"] = os.path.join(editor_path, "enforced-editing-wrapper") + env["VISUAL"] = os.path.join(editor_path, "enforced-editing-wrapper") + env["UDT_EDIT_WRAPPER_TEMPLATE_RE"] = ".*REPLACE THIS WITH ACTUAL INFORMATION.*" + env["UDT_EDIT_WRAPPER_FILE_DESCRIPTION"] = "bug report" # In external mua mode, attachments are lost (Reportbug bug: #679907) internal_mua = True - for cfgfile in ('/etc/reportbug.conf', '~/.reportbugrc'): + for cfgfile in ("/etc/reportbug.conf", "~/.reportbugrc"): cfgfile = os.path.expanduser(cfgfile) if not os.path.exists(cfgfile): continue - with open(cfgfile, 'r') as f: + with open(cfgfile, "r") as f: for line in f: line = line.strip() - if line in ('gnus', 'mutt', 'nmh') or line.startswith('mua '): + if line in ("gnus", "mutt", "nmh") or line.startswith("mua "): internal_mua = False break - cmd = ('reportbug', - '--no-check-available', - '--no-check-installed', - '--pseudo-header', 'User: ubuntu-devel@lists.ubuntu.com', - '--pseudo-header', 'Usertags: origin-ubuntu %s ubuntu-patch' - % devel, - '--tag', 'patch', - '--bts', 'debian', - '--include', body, - '--attach' if internal_mua else '--include', debdiff, - '--package-version', deb_version, - changelog.package) + cmd = ( + "reportbug", + "--no-check-available", + "--no-check-installed", + "--pseudo-header", + "User: ubuntu-devel@lists.ubuntu.com", + "--pseudo-header", + "Usertags: origin-ubuntu %s ubuntu-patch" % devel, + "--tag", + "patch", + "--bts", + "debian", + "--include", + body, + "--attach" if internal_mua else "--include", + debdiff, + "--package-version", + deb_version, + changelog.package, + ) check_call(cmd, env=env) def check_reportbug_config(): - fn = os.path.expanduser('~/.reportbugrc') + fn = os.path.expanduser("~/.reportbugrc") if os.path.exists(fn): return email = ubu_email()[1] - reportbugrc = """# Reportbug configuration generated by submittodebian(1) + reportbugrc = ( + """# Reportbug configuration generated by submittodebian(1) # See reportbug.conf(5) for the configuration file format. # Use Debian's reportbug SMTP Server: @@ -195,12 +209,15 @@ no-cc #smtphost smtp.googlemail.com:587 #smtpuser "@gmail.com" #smtptls -""" % email +""" + % email + ) - with open(fn, 'w') as f: + with open(fn, "w") as f: f.write(reportbugrc) - Logger.info("""\ + Logger.info( + """\ You have not configured reportbug. Assuming this is the first time you have used it. Writing a ~/.reportbugrc that will use Debian's mail server, and CC the bug to you at <%s> @@ -211,26 +228,32 @@ the bug to you at <%s> If this is not correct, please exit now and edit ~/.reportbugrc or run reportbug --configure for its configuration wizard. -""" % (email, reportbugrc.strip())) +""" + % (email, reportbugrc.strip()) + ) if YesNoQuestion().ask("Continue submitting this bug", "yes") == "no": sys.exit(1) def main(): - description = 'Submit the Ubuntu changes in a package to Debian. ' + \ - 'Run inside an unpacked Ubuntu source package.' + description = ( + "Submit the Ubuntu changes in a package to Debian. " + + "Run inside an unpacked Ubuntu source package." + ) parser = optparse.OptionParser(description=description) parser.parse_args() - if not os.path.exists('/usr/bin/reportbug'): - Logger.error("This utility requires the «reportbug» package, which isn't " - "currently installed.") + if not os.path.exists("/usr/bin/reportbug"): + Logger.error( + "This utility requires the «reportbug» package, which isn't currently installed." + ) sys.exit(1) check_reportbug_config() - changelog_file = (check_file('debian/changelog', critical=False) or - check_file('../debian/changelog')) + changelog_file = check_file("debian/changelog", critical=False) or check_file( + "../debian/changelog" + ) with open(changelog_file) as f: changelog = Changelog(f.read()) @@ -238,13 +261,13 @@ def main(): bug_body = get_bug_body(changelog) tmpdir = mkdtemp() - body = os.path.join(tmpdir, 'bug_body') - with open(body, 'wb') as f: - f.write(bug_body.encode('utf-8')) + body = os.path.join(tmpdir, "bug_body") + with open(body, "wb") as f: + f.write(bug_body.encode("utf-8")) - restore_maintainer('debian') + restore_maintainer("debian") build_source_package() - update_maintainer('debian') + update_maintainer("debian") debdiff = gen_debdiff(tmpdir, changelog) @@ -252,7 +275,7 @@ def main(): # reverted in the most recent build build_source_package() - EditFile(debdiff, 'debdiff').edit(optional=True) + EditFile(debdiff, "debdiff").edit(optional=True) submit_bugreport(body, debdiff, deb_version, changelog) os.unlink(body) @@ -260,5 +283,5 @@ def main(): shutil.rmtree(tmpdir) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/syncpackage b/syncpackage index 67c538c..aae1899 100755 --- a/syncpackage +++ b/syncpackage @@ -32,27 +32,30 @@ import urllib.request from lazr.restfulclient.errors import HTTPError -from ubuntutools.archive import (DebianSourcePackage, UbuntuSourcePackage, - DownloadError) +from ubuntutools.archive import DebianSourcePackage, UbuntuSourcePackage, DownloadError from ubuntutools.config import UDTConfig, ubu_email from ubuntutools.lp import udtexceptions -from ubuntutools.lp.lpapicache import (Distribution, Launchpad, PersonTeam, - SourcePackagePublishingHistory) +from ubuntutools.lp.lpapicache import ( + Distribution, + Launchpad, + PersonTeam, + SourcePackagePublishingHistory, +) from ubuntutools.misc import split_release_pocket from ubuntutools.question import YesNoQuestion -from ubuntutools.requestsync.mail import ( - get_debian_srcpkg as requestsync_mail_get_debian_srcpkg) +from ubuntutools.requestsync.mail import get_debian_srcpkg as requestsync_mail_get_debian_srcpkg from ubuntutools.requestsync.lp import get_debian_srcpkg, get_ubuntu_srcpkg from ubuntutools.version import Version from ubuntutools import getLogger + Logger = getLogger() def remove_signature(dscname): - '''Removes the signature from a .dsc file if the .dsc file is signed.''' + """Removes the signature from a .dsc file if the .dsc file is signed.""" - dsc_file = open(dscname, encoding='utf-8') + dsc_file = open(dscname, encoding="utf-8") if dsc_file.readline().strip() == "-----BEGIN PGP SIGNED MESSAGE-----": unsigned_file = [] # search until begin of body found @@ -67,14 +70,14 @@ def remove_signature(dscname): unsigned_file.append(line) dsc_file.close() - dsc_file = open(dscname, "w", encoding='utf-8') + dsc_file = open(dscname, "w", encoding="utf-8") dsc_file.writelines(unsigned_file) dsc_file.close() def add_fixed_bugs(changes, bugs): - '''Add additional Launchpad bugs to the list of fixed bugs in changes - file.''' + """Add additional Launchpad bugs to the list of fixed bugs in changes + file.""" changes = [line for line in changes.split("\n") if line.strip() != ""] # Remove duplicates @@ -93,12 +96,23 @@ def add_fixed_bugs(changes, bugs): return "\n".join(changes + [""]) -def sync_dsc(src_pkg, debian_dist, release, name, email, bugs, ubuntu_mirror, - keyid=None, simulate=False, force=False, fakesync=False): - '''Local sync, trying to emulate sync-source.py +def sync_dsc( + src_pkg, + debian_dist, + release, + name, + email, + bugs, + ubuntu_mirror, + keyid=None, + simulate=False, + force=False, + fakesync=False, +): + """Local sync, trying to emulate sync-source.py Grabs a source package, replaces the .orig.tar with the one from Ubuntu, if necessary, writes a sync-appropriate .changes file, and signs it. - ''' + """ uploader = name + " <" + email + ">" @@ -106,52 +120,59 @@ def sync_dsc(src_pkg, debian_dist, release, name, email, bugs, ubuntu_mirror, try: ubuntu_series, ubuntu_pocket = split_release_pocket(release) - ubuntu_source = get_ubuntu_srcpkg(src_pkg.source, ubuntu_series, - ubuntu_pocket) + ubuntu_source = get_ubuntu_srcpkg(src_pkg.source, ubuntu_series, ubuntu_pocket) ubuntu_ver = Version(ubuntu_source.getVersion()) - ubu_pkg = UbuntuSourcePackage(src_pkg.source, ubuntu_ver.full_version, - ubuntu_source.getComponent(), - mirrors=[ubuntu_mirror]) + ubu_pkg = UbuntuSourcePackage( + src_pkg.source, + ubuntu_ver.full_version, + ubuntu_source.getComponent(), + mirrors=[ubuntu_mirror], + ) need_orig = ubuntu_ver.upstream_version != new_ver.upstream_version except udtexceptions.PackageNotFoundException: - ubuntu_ver = Version('~') + ubuntu_ver = Version("~") ubu_pkg = None need_orig = True - Logger.info('%s does not exist in Ubuntu.', name) + Logger.info("%s does not exist in Ubuntu.", name) - Logger.debug('Source %s: current version %s, new version %s', - src_pkg.source, ubuntu_ver, new_ver) - Logger.debug('Needs source tarball: %s', str(need_orig)) + Logger.debug( + "Source %s: current version %s, new version %s", src_pkg.source, ubuntu_ver, new_ver + ) + Logger.debug("Needs source tarball: %s", str(need_orig)) cur_ver = ubuntu_ver.get_related_debian_version() if ubuntu_ver.is_modified_in_ubuntu(): if not force: - Logger.error('--force is required to discard Ubuntu changes.') + Logger.error("--force is required to discard Ubuntu changes.") sys.exit(1) - Logger.warning('Overwriting modified Ubuntu version %s, ' - 'setting current version to %s', - ubuntu_ver.full_version, cur_ver.full_version) + Logger.warning( + "Overwriting modified Ubuntu version %s, setting current version to %s", + ubuntu_ver.full_version, + cur_ver.full_version, + ) if simulate: return try: src_pkg.pull() except DownloadError as e: - Logger.error('Failed to download: %s', str(e)) + Logger.error("Failed to download: %s", str(e)) sys.exit(1) src_pkg.unpack() needs_fakesync = not (need_orig or ubu_pkg.verify_orig()) if needs_fakesync and fakesync: - Logger.warning('Performing a fakesync') + Logger.warning("Performing a fakesync") elif not needs_fakesync and fakesync: - Logger.error('Fakesync not required, aborting.') + Logger.error("Fakesync not required, aborting.") sys.exit(1) elif needs_fakesync and not fakesync: - Logger.error('The checksums of the Debian and Ubuntu packages ' - 'mismatch. A fake sync using --fakesync is required.') + Logger.error( + "The checksums of the Debian and Ubuntu packages " + "mismatch. A fake sync using --fakesync is required." + ) sys.exit(1) if fakesync: @@ -159,47 +180,50 @@ def sync_dsc(src_pkg, debian_dist, release, name, email, bugs, ubuntu_mirror, try: ubu_pkg.pull() except DownloadError as e: - Logger.error('Failed to download: %s', str(e)) + Logger.error("Failed to download: %s", str(e)) sys.exit(1) # change into package directory - directory = src_pkg.source + '-' + new_ver.upstream_version - Logger.debug('cd' + directory) + directory = src_pkg.source + "-" + new_ver.upstream_version + Logger.debug("cd" + directory) os.chdir(directory) # read Debian distribution from debian/changelog if not specified if debian_dist is None: - line = open("debian/changelog", encoding='utf-8').readline() + line = open("debian/changelog", encoding="utf-8").readline() debian_dist = line.split(" ")[2].strip(";") if not fakesync: # create the changes file - changes_filename = "%s_%s_source.changes" % \ - (src_pkg.source, new_ver.strip_epoch()) - cmd = ["dpkg-genchanges", "-S", "-v" + cur_ver.full_version, - "-DDistribution=" + release, - "-DOrigin=debian/" + debian_dist, - "-e" + uploader] + changes_filename = "%s_%s_source.changes" % (src_pkg.source, new_ver.strip_epoch()) + cmd = [ + "dpkg-genchanges", + "-S", + "-v" + cur_ver.full_version, + "-DDistribution=" + release, + "-DOrigin=debian/" + debian_dist, + "-e" + uploader, + ] if need_orig: cmd.append("-sa") else: cmd.append("-sd") if not Logger.isEnabledFor(logging.DEBUG): cmd += ["-q"] - Logger.debug(' '.join(cmd) + '> ../' + changes_filename) - changes = subprocess.check_output(cmd, encoding='utf-8') + Logger.debug(" ".join(cmd) + "> ../" + changes_filename) + changes = subprocess.check_output(cmd, encoding="utf-8") # Add additional bug numbers if len(bugs) > 0: changes = add_fixed_bugs(changes, bugs) # remove extracted (temporary) files - Logger.debug('cd ..') - os.chdir('..') + Logger.debug("cd ..") + os.chdir("..") shutil.rmtree(directory, True) # write changes file - changes_file = open(changes_filename, "w", encoding='utf-8') + changes_file = open(changes_filename, "w", encoding="utf-8") changes_file.writelines(changes) changes_file.close() @@ -209,48 +233,44 @@ def sync_dsc(src_pkg, debian_dist, release, name, email, bugs, ubuntu_mirror, cmd = ["debsign", changes_filename] if keyid is not None: cmd.insert(1, "-k" + keyid) - Logger.debug(' '.join(cmd)) + Logger.debug(" ".join(cmd)) subprocess.check_call(cmd) else: # Create fakesync changelog entry new_ver = Version(new_ver.full_version + "fakesync1") - changes_filename = "%s_%s_source.changes" % \ - (src_pkg.source, new_ver.strip_epoch()) + changes_filename = "%s_%s_source.changes" % (src_pkg.source, new_ver.strip_epoch()) if len(bugs) > 0: - message = "Fake sync due to mismatching orig tarball (LP: %s)." % \ - (", ".join(["#" + str(b) for b in bugs])) + message = "Fake sync due to mismatching orig tarball (LP: %s)." % ( + ", ".join(["#" + str(b) for b in bugs]) + ) else: message = "Fake sync due to mismatching orig tarball." - cmd = ['dch', '-v', new_ver.full_version, '--force-distribution', - '-D', release, message] - env = {'DEBFULLNAME': name, 'DEBEMAIL': email} - Logger.debug(' '.join(cmd)) + cmd = ["dch", "-v", new_ver.full_version, "--force-distribution", "-D", release, message] + env = {"DEBFULLNAME": name, "DEBEMAIL": email} + Logger.debug(" ".join(cmd)) subprocess.check_call(cmd, env=env) # update the Maintainer field cmd = ["update-maintainer"] if not Logger.isEnabledFor(logging.DEBUG): cmd.append("-q") - Logger.debug(' '.join(cmd)) + Logger.debug(" ".join(cmd)) subprocess.check_call(cmd) # Build source package - cmd = ["debuild", "--no-lintian", "-nc", "-S", - "-v" + cur_ver.full_version] + cmd = ["debuild", "--no-lintian", "-nc", "-S", "-v" + cur_ver.full_version] if need_orig: - cmd += ['-sa'] + cmd += ["-sa"] if keyid: cmd += ["-k" + keyid] - Logger.debug(' '.join(cmd)) + Logger.debug(" ".join(cmd)) returncode = subprocess.call(cmd) if returncode != 0: - Logger.error('Source-only build with debuild failed. ' - 'Please check build log above.') + Logger.error("Source-only build with debuild failed. Please check build log above.") sys.exit(1) -def fetch_source_pkg(package, dist, version, component, ubuntu_release, - mirror): +def fetch_source_pkg(package, dist, version, component, ubuntu_release, mirror): """Download the specified source package. dist, version, component, mirror can all be None. """ @@ -259,11 +279,11 @@ def fetch_source_pkg(package, dist, version, component, ubuntu_release, else: mirrors = [mirror] - if package.endswith('.dsc'): + if package.endswith(".dsc"): return DebianSourcePackage(dscfile=package, mirrors=mirrors) if dist is None: - dist = 'unstable' + dist = "unstable" requested_version = version if type(version) == str: @@ -272,19 +292,20 @@ def fetch_source_pkg(package, dist, version, component, ubuntu_release, if version is None or component is None: try: debian_srcpkg = get_debian_srcpkg(package, dist) - except (udtexceptions.PackageNotFoundException, - udtexceptions.SeriesNotFoundException) as e: + except ( + udtexceptions.PackageNotFoundException, + udtexceptions.SeriesNotFoundException, + ) as e: Logger.error(str(e)) sys.exit(1) if version is None: version = Version(debian_srcpkg.getVersion()) try: ubuntu_series, ubuntu_pocket = split_release_pocket(ubuntu_release) - ubuntu_srcpkg = get_ubuntu_srcpkg(package, ubuntu_series, - ubuntu_pocket) + ubuntu_srcpkg = get_ubuntu_srcpkg(package, ubuntu_series, ubuntu_pocket) ubuntu_version = Version(ubuntu_srcpkg.getVersion()) except udtexceptions.PackageNotFoundException: - ubuntu_version = Version('~') + ubuntu_version = Version("~") except udtexceptions.SeriesNotFoundException as e: Logger.error(str(e)) sys.exit(1) @@ -294,72 +315,84 @@ def fetch_source_pkg(package, dist, version, component, ubuntu_release, if requested_version is None: version = Version(debian_srcpkg.getVersion()) if ubuntu_version >= version: - Logger.error("Version in Debian %s (%s) isn't newer than " - "Ubuntu %s (%s)", - version, dist, ubuntu_version, ubuntu_release) + Logger.error( + "Version in Debian %s (%s) isn't newer than Ubuntu %s (%s)", + version, + dist, + ubuntu_version, + ubuntu_release, + ) sys.exit(1) if component is None: component = debian_srcpkg.getComponent() - assert component in ('main', 'contrib', 'non-free') + assert component in ("main", "contrib", "non-free") - return DebianSourcePackage(package, version.full_version, component, - mirrors=mirrors) + return DebianSourcePackage(package, version.full_version, component, mirrors=mirrors) def copy(src_pkg, release, bugs, sponsoree=None, simulate=False, force=False): """Copy a source package from Debian to Ubuntu using the Launchpad API.""" - ubuntu = Distribution('ubuntu') - debian_archive = Distribution('debian').getArchive() + ubuntu = Distribution("ubuntu") + debian_archive = Distribution("debian").getArchive() ubuntu_archive = ubuntu.getArchive() if release is None: ubuntu_series = ubuntu.getDevelopmentSeries().name - ubuntu_pocket = 'Release' + ubuntu_pocket = "Release" else: ubuntu_series, ubuntu_pocket = split_release_pocket(release) # Ensure that the provided Debian version actually exists. try: debian_spph = SourcePackagePublishingHistory( - debian_archive.getPublishedSources( - source_name=src_pkg.source, - version=src_pkg.version.full_version, - exact_match=True)[0] - ) + debian_archive.getPublishedSources( + source_name=src_pkg.source, version=src_pkg.version.full_version, exact_match=True + )[0] + ) except IndexError: - Logger.error('Debian version %s has not been picked up by LP yet. ' - 'Please try again later.', - src_pkg.version) + Logger.error( + "Debian version %s has not been picked up by LP yet. Please try again later.", + src_pkg.version, + ) sys.exit(1) try: - ubuntu_spph = get_ubuntu_srcpkg(src_pkg.source, - ubuntu_series, ubuntu_pocket) - ubuntu_pkg = UbuntuSourcePackage(src_pkg.source, - ubuntu_spph.getVersion(), - ubuntu_spph.getComponent(), - mirrors=[]) + ubuntu_spph = get_ubuntu_srcpkg(src_pkg.source, ubuntu_series, ubuntu_pocket) + ubuntu_pkg = UbuntuSourcePackage( + src_pkg.source, ubuntu_spph.getVersion(), ubuntu_spph.getComponent(), mirrors=[] + ) - Logger.info('Source %s -> %s/%s: current version %s, new version %s', - src_pkg.source, ubuntu_series, ubuntu_pocket, - ubuntu_pkg.version, src_pkg.version) + Logger.info( + "Source %s -> %s/%s: current version %s, new version %s", + src_pkg.source, + ubuntu_series, + ubuntu_pocket, + ubuntu_pkg.version, + src_pkg.version, + ) ubuntu_version = Version(ubuntu_pkg.version.full_version) base_version = ubuntu_version.get_related_debian_version() if not force and ubuntu_version.is_modified_in_ubuntu(): - Logger.error('--force is required to discard Ubuntu changes.') + Logger.error("--force is required to discard Ubuntu changes.") sys.exit(1) # Check whether a fakesync would be required. if not src_pkg.dsc.compare_dsc(ubuntu_pkg.dsc): - Logger.error('The checksums of the Debian and Ubuntu packages ' - 'mismatch. A fake sync using --fakesync is required.') + Logger.error( + "The checksums of the Debian and Ubuntu packages " + "mismatch. A fake sync using --fakesync is required." + ) sys.exit(1) except udtexceptions.PackageNotFoundException: - base_version = Version('~') - Logger.info('Source %s -> %s/%s: not in Ubuntu, new version %s', - src_pkg.source, ubuntu_series, ubuntu_pocket, - src_pkg.version) + base_version = Version("~") + Logger.info( + "Source %s -> %s/%s: not in Ubuntu, new version %s", + src_pkg.source, + ubuntu_series, + ubuntu_pocket, + src_pkg.version, + ) changes = debian_spph.getChangelog(since_version=base_version) if changes: @@ -370,8 +403,7 @@ def copy(src_pkg, release, bugs, sponsoree=None, simulate=False, force=False): return if sponsoree: - Logger.info("Sponsoring this sync for %s (%s)", - sponsoree.display_name, sponsoree.name) + Logger.info("Sponsoring this sync for %s (%s)", sponsoree.display_name, sponsoree.name) answer = YesNoQuestion().ask("Sync this package", "no") if answer != "yes": return @@ -384,65 +416,65 @@ def copy(src_pkg, release, bugs, sponsoree=None, simulate=False, force=False): to_series=ubuntu_series, to_pocket=ubuntu_pocket, include_binaries=False, - sponsored=sponsoree) + sponsored=sponsoree, + ) except HTTPError as error: - Logger.error("HTTP Error %s: %s", error.response.status, - error.response.reason) + Logger.error("HTTP Error %s: %s", error.response.status, error.response.reason) Logger.error(error.content) sys.exit(1) - Logger.info('Request succeeded; you should get an e-mail once it is ' - 'processed.') + Logger.info("Request succeeded; you should get an e-mail once it is processed.") bugs = sorted(set(bugs)) if bugs: - Logger.info("Launchpad bugs to be closed: %s", - ', '.join(str(bug) for bug in bugs)) - Logger.info('Please wait for the sync to be successful before ' - 'closing bugs.') + Logger.info("Launchpad bugs to be closed: %s", ", ".join(str(bug) for bug in bugs)) + Logger.info("Please wait for the sync to be successful before closing bugs.") answer = YesNoQuestion().ask("Close bugs", "yes") if answer == "yes": - close_bugs(bugs, src_pkg.source, src_pkg.version.full_version, - changes, sponsoree) + close_bugs(bugs, src_pkg.source, src_pkg.version.full_version, changes, sponsoree) def is_blacklisted(query): - """"Determine if package "query" is in the sync blacklist + """Determine if package "query" is in the sync blacklist Returns tuple of (blacklisted, comments) blacklisted is one of False, 'CURRENT', 'ALWAYS' """ - series = Launchpad.distributions['ubuntu'].current_series + series = Launchpad.distributions["ubuntu"].current_series lp_comments = series.getDifferenceComments(source_package_name=query) blacklisted = False - comments = ['%s\n -- %s %s' - % (c.body_text, c.comment_author.name, - c.comment_date.strftime('%a, %d %b %Y %H:%M:%S +0000')) - for c in lp_comments] + comments = [ + "%s\n -- %s %s" + % ( + c.body_text, + c.comment_author.name, + c.comment_date.strftime("%a, %d %b %Y %H:%M:%S +0000"), + ) + for c in lp_comments + ] for diff in series.getDifferencesTo(source_package_name_filter=query): - if (diff.status == 'Blacklisted current version' - and blacklisted != 'ALWAYS'): - blacklisted = 'CURRENT' - if diff.status == 'Blacklisted always': - blacklisted = 'ALWAYS' + if diff.status == "Blacklisted current version" and blacklisted != "ALWAYS": + blacklisted = "CURRENT" + if diff.status == "Blacklisted always": + blacklisted = "ALWAYS" # Old blacklist: - url = 'http://people.canonical.com/~ubuntu-archive/sync-blacklist.txt' + url = "http://people.canonical.com/~ubuntu-archive/sync-blacklist.txt" with urllib.request.urlopen(url) as f: applicable_lines = [] for line in f: - line = line.decode('utf-8') + line = line.decode("utf-8") if not line.strip(): applicable_lines = [] continue applicable_lines.append(line) try: - line = line[:line.index('#')] + line = line[: line.index("#")] except ValueError: pass source = line.strip() if source and fnmatch.fnmatch(query, source): comments += ["From sync-blacklist.txt:"] + applicable_lines - blacklisted = 'ALWAYS' + blacklisted = "ALWAYS" break return (blacklisted, comments) @@ -450,12 +482,10 @@ def is_blacklisted(query): def close_bugs(bugs, package, version, changes, sponsoree): """Close the correct task on all bugs, with changes""" - ubuntu = Launchpad.distributions['ubuntu'] - message = ("This bug was fixed in the package %s - %s" - % (package, version)) + ubuntu = Launchpad.distributions["ubuntu"] + message = "This bug was fixed in the package %s - %s" % (package, version) if sponsoree: - message += '\nSponsored for %s (%s)' % (sponsoree.display_name, - sponsoree.name) + message += "\nSponsored for %s (%s)" % (sponsoree.display_name, sponsoree.name) if changes: message += "\n\n---------------\n" + changes for bug in bugs: @@ -464,11 +494,12 @@ def close_bugs(bugs, package, version, changes, sponsoree): bug = bug.duplicate_of for task in bug.bug_tasks: target = task.target - if target == ubuntu or (target.name == package and - getattr(target, 'distribution', None) == ubuntu): - if task.status != 'Fix Released': + if target == ubuntu or ( + target.name == package and getattr(target, "distribution", None) == ubuntu + ): + if task.status != "Fix Released": Logger.info("Closed bug %s", task.web_link) - task.status = 'Fix Released' + task.status = "Fix Released" task.lp_save() bug.newMessage(content=message) break @@ -483,77 +514,116 @@ def parse(): epilog = "See %s(1) for more info." % os.path.basename(sys.argv[0]) parser = optparse.OptionParser(usage=usage, epilog=epilog) - parser.add_option("-d", "--distribution", - help="Debian distribution to sync from.") - parser.add_option("-r", "--release", - help="Specify target Ubuntu release.") - parser.add_option("-V", "--debian-version", - help="Specify the version to sync from.") - parser.add_option("-c", "--component", - help="Specify the Debian component to sync from.") - parser.add_option("-b", "--bug", metavar="BUG", - dest="bugs", action="append", default=list(), - help="Mark Launchpad bug BUG as being fixed by this " - "upload.") - parser.add_option("-s", "--sponsor", metavar="USERNAME", - dest="sponsoree", default=None, - help="Sponsor the sync for USERNAME (a Launchpad " - "username).") - parser.add_option("-v", "--verbose", - action="store_true", default=False, - help="Display more progress information.") - parser.add_option("-F", "--fakesync", - action="store_true", default=False, - help="Perform a fakesync (a sync where Debian and " - "Ubuntu have a .orig.tar mismatch). " - "This implies --no-lp and will leave a signed " - ".changes file for you to upload.") - parser.add_option("-f", "--force", - action="store_true", default=False, - help="Force sync over the top of Ubuntu changes.") - parser.add_option('--no-conf', - default=False, action='store_true', - help="Don't read config files or environment variables.") - parser.add_option('-l', '--lpinstance', metavar='INSTANCE', - help='Launchpad instance to connect to ' - '(default: production).') - parser.add_option('--simulate', - default=False, action='store_true', - help="Show what would be done, but don't actually do " - "it.") + parser.add_option("-d", "--distribution", help="Debian distribution to sync from.") + parser.add_option("-r", "--release", help="Specify target Ubuntu release.") + parser.add_option("-V", "--debian-version", help="Specify the version to sync from.") + parser.add_option("-c", "--component", help="Specify the Debian component to sync from.") + parser.add_option( + "-b", + "--bug", + metavar="BUG", + dest="bugs", + action="append", + default=list(), + help="Mark Launchpad bug BUG as being fixed by this upload.", + ) + parser.add_option( + "-s", + "--sponsor", + metavar="USERNAME", + dest="sponsoree", + default=None, + help="Sponsor the sync for USERNAME (a Launchpad username).", + ) + parser.add_option( + "-v", + "--verbose", + action="store_true", + default=False, + help="Display more progress information.", + ) + parser.add_option( + "-F", + "--fakesync", + action="store_true", + default=False, + help="Perform a fakesync (a sync where Debian and " + "Ubuntu have a .orig.tar mismatch). " + "This implies --no-lp and will leave a signed " + ".changes file for you to upload.", + ) + parser.add_option( + "-f", + "--force", + action="store_true", + default=False, + help="Force sync over the top of Ubuntu changes.", + ) + parser.add_option( + "--no-conf", + default=False, + action="store_true", + help="Don't read config files or environment variables.", + ) + parser.add_option( + "-l", + "--lpinstance", + metavar="INSTANCE", + help="Launchpad instance to connect to (default: production).", + ) + parser.add_option( + "--simulate", + default=False, + action="store_true", + help="Show what would be done, but don't actually do it.", + ) no_lp = optparse.OptionGroup( - parser, "Local sync preparation options", + parser, + "Local sync preparation options", "Options that only apply when using --no-lp. " "WARNING: The use of --no-lp is not recommended for uploads " "targeted at Ubuntu. " - "The archive-admins discourage its use, except for fakesyncs.") - no_lp.add_option("--no-lp", - dest="lp", action="store_false", default=True, - help="Construct sync locally, rather than letting " - "Launchpad copy the package directly. " - "It will leave a signed .changes file for you to " - "upload.") - no_lp.add_option("-n", "--uploader-name", - help="Use UPLOADER_NAME as the name of the maintainer " - "for this upload.") - no_lp.add_option("-e", "--uploader-email", - help="Use UPLOADER_EMAIL as email address of the " - "maintainer for this upload.") - no_lp.add_option("-k", "--key", - dest="keyid", - help="Specify the key ID to be used for signing.") - no_lp.add_option('--dont-sign', - dest='keyid', action='store_false', - help='Do not sign the upload.') - no_lp.add_option('-D', '--debian-mirror', metavar='DEBIAN_MIRROR', - help='Preferred Debian mirror ' - '(default: %s)' - % UDTConfig.defaults['DEBIAN_MIRROR']) - no_lp.add_option('-U', '--ubuntu-mirror', metavar='UBUNTU_MIRROR', - help='Preferred Ubuntu mirror ' - '(default: %s)' - % UDTConfig.defaults['UBUNTU_MIRROR']) + "The archive-admins discourage its use, except for fakesyncs.", + ) + no_lp.add_option( + "--no-lp", + dest="lp", + action="store_false", + default=True, + help="Construct sync locally, rather than letting " + "Launchpad copy the package directly. " + "It will leave a signed .changes file for you to " + "upload.", + ) + no_lp.add_option( + "-n", + "--uploader-name", + help="Use UPLOADER_NAME as the name of the maintainer for this upload.", + ) + no_lp.add_option( + "-e", + "--uploader-email", + help="Use UPLOADER_EMAIL as email address of the maintainer for this upload.", + ) + no_lp.add_option( + "-k", "--key", dest="keyid", help="Specify the key ID to be used for signing." + ) + no_lp.add_option( + "--dont-sign", dest="keyid", action="store_false", help="Do not sign the upload." + ) + no_lp.add_option( + "-D", + "--debian-mirror", + metavar="DEBIAN_MIRROR", + help="Preferred Debian mirror (default: %s)" % UDTConfig.defaults["DEBIAN_MIRROR"], + ) + no_lp.add_option( + "-U", + "--ubuntu-mirror", + metavar="UBUNTU_MIRROR", + help="Preferred Ubuntu mirror (default: %s)" % UDTConfig.defaults["UBUNTU_MIRROR"], + ) parser.add_option_group(no_lp) (options, args) = parser.parse_args() @@ -562,58 +632,56 @@ def parse(): options.lp = False if len(args) == 0: - parser.error('No .dsc URL/path or package name specified.') + parser.error("No .dsc URL/path or package name specified.") if len(args) > 1: - parser.error('Multiple .dsc URLs/paths or package names specified: ' - + ', '.join(args)) + parser.error("Multiple .dsc URLs/paths or package names specified: " + ", ".join(args)) try: options.bugs = [int(b) for b in options.bugs] except TypeError: - parser.error('Invalid bug number(s) specified.') + parser.error("Invalid bug number(s) specified.") if options.component not in (None, "main", "contrib", "non-free"): - parser.error('%s is not a valid Debian component. ' - 'It should be one of main, contrib, or non-free.' - % options.component) + parser.error( + "%s is not a valid Debian component. " + "It should be one of main, contrib, or non-free." % options.component + ) if options.lp and options.uploader_name: - parser.error('Uploader name can only be overridden using --no-lp.') + parser.error("Uploader name can only be overridden using --no-lp.") if options.lp and options.uploader_email: - parser.error('Uploader email address can only be overridden using ' - '--no-lp.') + parser.error("Uploader email address can only be overridden using --no-lp.") # --key, --dont-sign, --debian-mirror, and --ubuntu-mirror are just # ignored with options.lp, and do not require warnings. if options.lp: - if args[0].endswith('.dsc'): - parser.error('.dsc files can only be synced using --no-lp.') + if args[0].endswith(".dsc"): + parser.error(".dsc files can only be synced using --no-lp.") return (options, args[0]) def main(): - '''Handle parameters and get the ball rolling''' + """Handle parameters and get the ball rolling""" (options, package) = parse() if options.verbose: - Logger.setLevel('DEBUG') + Logger.setLevel("DEBUG") config = UDTConfig(options.no_conf) if options.debian_mirror is None: - options.debian_mirror = config.get_value('DEBIAN_MIRROR') + options.debian_mirror = config.get_value("DEBIAN_MIRROR") if options.ubuntu_mirror is None: - options.ubuntu_mirror = config.get_value('UBUNTU_MIRROR') + options.ubuntu_mirror = config.get_value("UBUNTU_MIRROR") if options.keyid is None: - options.keyid = config.get_value('KEYID') + options.keyid = config.get_value("KEYID") if options.lpinstance is None: - options.lpinstance = config.get_value('LPINSTANCE') + options.lpinstance = config.get_value("LPINSTANCE") try: # devel for copyPackage and changelogUrl - kwargs = {'service': options.lpinstance, - 'api_version': 'devel'} + kwargs = {"service": options.lpinstance, "api_version": "devel"} if options.lp and not options.simulate: Launchpad.login(**kwargs) else: @@ -626,18 +694,19 @@ def main(): options.release = "%s-proposed" % ubuntu.current_series.name if not options.fakesync and not options.lp: - Logger.warning("The use of --no-lp is not recommended for uploads " - "targeted at Ubuntu. " - "The archive-admins discourage its use, except for " - "fakesyncs.") + Logger.warning( + "The use of --no-lp is not recommended for uploads " + "targeted at Ubuntu. " + "The archive-admins discourage its use, except for " + "fakesyncs." + ) sponsoree = None if options.sponsoree: try: sponsoree = PersonTeam(options.sponsoree) except KeyError: - Logger.error('Cannot find the username "%s" in Launchpad.', - options.sponsoree) + Logger.error('Cannot find the username "%s" in Launchpad.', options.sponsoree) sys.exit(1) if sponsoree and options.uploader_name is None: @@ -650,44 +719,54 @@ def main(): options.uploader_email = sponsoree.preferred_email_address.email except ValueError: if not options.lp: - Logger.error("%s doesn't have a publicly visible e-mail " - "address in LP, please provide one " - "--uploader-email option", sponsoree.display_name) + Logger.error( + "%s doesn't have a publicly visible e-mail " + "address in LP, please provide one " + "--uploader-email option", + sponsoree.display_name, + ) sys.exit(1) elif options.uploader_email is None: options.uploader_email = ubu_email(export=False)[1] - src_pkg = fetch_source_pkg(package, options.distribution, - options.debian_version, - options.component, - options.release, - options.debian_mirror) + src_pkg = fetch_source_pkg( + package, + options.distribution, + options.debian_version, + options.component, + options.release, + options.debian_mirror, + ) blacklisted, comments = is_blacklisted(src_pkg.source) blacklist_fail = False if blacklisted: messages = [] - if blacklisted == 'CURRENT': - Logger.debug("Source package %s is temporarily blacklisted " - "(blacklisted_current). " - "Ubuntu ignores these for now. " - "See also LP: #841372", src_pkg.source) + if blacklisted == "CURRENT": + Logger.debug( + "Source package %s is temporarily blacklisted " + "(blacklisted_current). " + "Ubuntu ignores these for now. " + "See also LP: #841372", + src_pkg.source, + ) else: if options.fakesync: messages += ["Doing a fakesync, overriding blacklist."] else: blacklist_fail = True - messages += ["If this package needs a fakesync, " - "use --fakesync", - "If you think this package shouldn't be " - "blacklisted, please file a bug explaining your " - "reasoning and subscribe ~ubuntu-archive."] + messages += [ + "If this package needs a fakesync, use --fakesync", + "If you think this package shouldn't be " + "blacklisted, please file a bug explaining your " + "reasoning and subscribe ~ubuntu-archive.", + ] if blacklist_fail: Logger.error("Source package %s is blacklisted.", src_pkg.source) - elif blacklisted == 'ALWAYS': - Logger.info(u"Source package %s is blacklisted.", src_pkg.source) + elif blacklisted == "ALWAYS": + Logger.info("Source package %s is blacklisted.", src_pkg.source) if messages: for message in messages: for line in textwrap.wrap(message): @@ -703,18 +782,26 @@ def main(): sys.exit(1) if options.lp: - copy(src_pkg, options.release, options.bugs, sponsoree, - options.simulate, options.force) + copy(src_pkg, options.release, options.bugs, sponsoree, options.simulate, options.force) else: - os.environ['DEB_VENDOR'] = 'Ubuntu' - sync_dsc(src_pkg, options.distribution, options.release, - options.uploader_name, options.uploader_email, options.bugs, - options.ubuntu_mirror, options.keyid, options.simulate, - options.force, options.fakesync) + os.environ["DEB_VENDOR"] = "Ubuntu" + sync_dsc( + src_pkg, + options.distribution, + options.release, + options.uploader_name, + options.uploader_email, + options.bugs, + options.ubuntu_mirror, + options.keyid, + options.simulate, + options.force, + options.fakesync, + ) if __name__ == "__main__": try: main() except KeyboardInterrupt: - Logger.info('User abort.') + Logger.info("User abort.") diff --git a/ubuntu-build b/ubuntu-build index d07a71c..5d70aa1 100755 --- a/ubuntu-build +++ b/ubuntu-build @@ -26,14 +26,17 @@ import sys from optparse import OptionGroup from optparse import OptionParser -from ubuntutools.lp.udtexceptions import (SeriesNotFoundException, - PackageNotFoundException, - PocketDoesNotExistError,) +from ubuntutools.lp.udtexceptions import ( + SeriesNotFoundException, + PackageNotFoundException, + PocketDoesNotExistError, +) from ubuntutools.lp.lpapicache import Distribution, Launchpad, PersonTeam from launchpadlib.credentials import TokenAuthorizationException from ubuntutools.misc import split_release_pocket from ubuntutools import getLogger + Logger = getLogger() @@ -44,48 +47,84 @@ def main(): usage += "Only Launchpad Buildd Admins may rescore package builds." # Valid architectures. - valid_archs = set([ - "armel", "armhf", "arm64", "amd64", "hppa", "i386", "ia64", - "lpia", "powerpc", "ppc64el", "riscv64", "s390x", "sparc", - ]) + valid_archs = set( + [ + "armel", + "armhf", + "arm64", + "amd64", + "hppa", + "i386", + "ia64", + "lpia", + "powerpc", + "ppc64el", + "riscv64", + "s390x", + "sparc", + ] + ) # Prepare our option parser. opt_parser = OptionParser(usage) # Retry options - retry_rescore_options = OptionGroup(opt_parser, "Retry and rescore options", - "These options may only be used with " - "the 'retry' and 'rescore' operations.") - retry_rescore_options.add_option("-a", "--arch", type="string", - action="append", dest="architecture", - help="Rebuild or rescore a specific " - "architecture. Valid architectures " - "include: %s." % - ", ".join(valid_archs)) + retry_rescore_options = OptionGroup( + opt_parser, + "Retry and rescore options", + "These options may only be used with the 'retry' and 'rescore' operations.", + ) + retry_rescore_options.add_option( + "-a", + "--arch", + type="string", + action="append", + dest="architecture", + help="Rebuild or rescore a specific " + "architecture. Valid architectures " + "include: %s." % ", ".join(valid_archs), + ) # Batch processing options - batch_options = OptionGroup(opt_parser, "Batch processing", - "These options and parameter ordering is only " - "available in --batch mode.\nUsage: " - "ubuntu-build --batch [options] ...") - batch_options.add_option('--batch', - action='store_true', dest='batch', default=False, - help='Enable batch mode') - batch_options.add_option('--series', - action='store', dest='series', type='string', - help='Selects the Ubuntu series to operate on ' - '(default: current development series)') - batch_options.add_option('--retry', - action='store_true', dest='retry', default=False, - help='Retry builds (give-back).') - batch_options.add_option('--rescore', - action='store', dest='priority', type='int', - help='Rescore builds to .') - batch_options.add_option('--arch2', action='append', dest='architecture', - type='string', - help="Affect only 'architecture' (can be used " - "several times). Valid architectures are: %s." - % ', '.join(valid_archs)) + batch_options = OptionGroup( + opt_parser, + "Batch processing", + "These options and parameter ordering is only " + "available in --batch mode.\nUsage: " + "ubuntu-build --batch [options] ...", + ) + batch_options.add_option( + "--batch", action="store_true", dest="batch", default=False, help="Enable batch mode" + ) + batch_options.add_option( + "--series", + action="store", + dest="series", + type="string", + help="Selects the Ubuntu series to operate on (default: current development series)", + ) + batch_options.add_option( + "--retry", + action="store_true", + dest="retry", + default=False, + help="Retry builds (give-back).", + ) + batch_options.add_option( + "--rescore", + action="store", + dest="priority", + type="int", + help="Rescore builds to .", + ) + batch_options.add_option( + "--arch2", + action="append", + dest="architecture", + type="string", + help="Affect only 'architecture' (can be used " + "several times). Valid architectures are: %s." % ", ".join(valid_archs), + ) # Add the retry options to the main group. opt_parser.add_option_group(retry_rescore_options) @@ -121,8 +160,7 @@ def main(): # rebuild it and nothing else. if options.architecture: if options.architecture[0] not in valid_archs: - Logger.error("Invalid architecture specified: %s." - % options.architecture[0]) + Logger.error("Invalid architecture specified: %s." % options.architecture[0]) sys.exit(1) else: one_arch = True @@ -143,11 +181,11 @@ def main(): sys.exit(1) # Get the ubuntu archive - ubuntu_archive = Distribution('ubuntu').getArchive() + ubuntu_archive = Distribution("ubuntu").getArchive() # Get list of published sources for package in question. try: sources = ubuntu_archive.getSourcePackage(package, release, pocket) - distroseries = Distribution('ubuntu').getSeries(release) + distroseries = Distribution("ubuntu").getSeries(release) except (SeriesNotFoundException, PackageNotFoundException) as error: Logger.error(error) sys.exit(1) @@ -163,22 +201,29 @@ def main(): # are in place. me = PersonTeam.me if op == "rescore": - necessary_privs = me.isLpTeamMember('launchpad-buildd-admins') + necessary_privs = me.isLpTeamMember("launchpad-buildd-admins") if op == "retry": - necessary_privs = me.canUploadPackage(ubuntu_archive, distroseries, - sources.getPackageName(), - sources.getComponent(), - pocket=pocket) + necessary_privs = me.canUploadPackage( + ubuntu_archive, + distroseries, + sources.getPackageName(), + sources.getComponent(), + pocket=pocket, + ) - if op in ('rescore', 'retry') and not necessary_privs: - Logger.error("You cannot perform the %s operation on a %s " - "package as you do not have the permissions " - "to do this action." % (op, component)) + if op in ("rescore", "retry") and not necessary_privs: + Logger.error( + "You cannot perform the %s operation on a %s " + "package as you do not have the permissions " + "to do this action." % (op, component) + ) sys.exit(1) # Output details. - Logger.info("The source version for '%s' in %s (%s) is at %s." % - (package, release.capitalize(), component, version)) + Logger.info( + "The source version for '%s' in %s (%s) is at %s." + % (package, release.capitalize(), component, version) + ) Logger.info("Current build status for this package:") @@ -191,20 +236,20 @@ def main(): done = True Logger.info("%s: %s." % (build.arch_tag, build.buildstate)) - if op == 'rescore': + if op == "rescore": if build.can_be_rescored: # FIXME: make priority an option priority = 5000 - Logger.info('Rescoring build %s to %d...' % (build.arch_tag, priority)) + Logger.info("Rescoring build %s to %d..." % (build.arch_tag, priority)) build.rescore(score=priority) else: - Logger.info('Cannot rescore build on %s.' % build.arch_tag) - if op == 'retry': + Logger.info("Cannot rescore build on %s." % build.arch_tag) + if op == "retry": if build.can_be_retried: - Logger.info('Retrying build on %s...' % build.arch_tag) + Logger.info("Retrying build on %s..." % build.arch_tag) build.retry() else: - Logger.info('Cannot retry build on %s.' % build.arch_tag) + Logger.info("Cannot retry build on %s." % build.arch_tag) # We are done if done: @@ -227,29 +272,27 @@ def main(): release = options.series if not release: - release = (Distribution('ubuntu').getDevelopmentSeries().name - + '-proposed') + release = Distribution("ubuntu").getDevelopmentSeries().name + "-proposed" try: (release, pocket) = split_release_pocket(release) except PocketDoesNotExistError as error: Logger.error(error) sys.exit(1) - ubuntu_archive = Distribution('ubuntu').getArchive() + ubuntu_archive = Distribution("ubuntu").getArchive() try: - distroseries = Distribution('ubuntu').getSeries(release) + distroseries = Distribution("ubuntu").getSeries(release) except SeriesNotFoundException as error: Logger.error(error) sys.exit(1) me = PersonTeam.me # Check permisions (part 1): Rescoring can only be done by buildd admins - can_rescore = ((options.priority - and me.isLpTeamMember('launchpad-buildd-admins')) - or False) + can_rescore = (options.priority and me.isLpTeamMember("launchpad-buildd-admins")) or False if options.priority and not can_rescore: - Logger.error("You don't have the permissions to rescore " - "builds. Ignoring your rescore request.") + Logger.error( + "You don't have the permissions to rescore builds. Ignoring your rescore request." + ) for pkg in args: try: @@ -260,17 +303,19 @@ def main(): # Check permissions (part 2): check upload permissions for the source # package - can_retry = options.retry and me.canUploadPackage(ubuntu_archive, - distroseries, - pkg.getPackageName(), - pkg.getComponent()) + can_retry = options.retry and me.canUploadPackage( + ubuntu_archive, distroseries, pkg.getPackageName(), pkg.getComponent() + ) if options.retry and not can_retry: - Logger.error("You don't have the permissions to retry the " - "build of '%s'. Ignoring your request." - % pkg.getPackageName()) + Logger.error( + "You don't have the permissions to retry the " + "build of '%s'. Ignoring your request." % pkg.getPackageName() + ) - Logger.info("The source version for '%s' in '%s' (%s) is: %s" % - (pkg.getPackageName(), release, pocket, pkg.getVersion())) + Logger.info( + "The source version for '%s' in '%s' (%s) is: %s" + % (pkg.getPackageName(), release, pocket, pkg.getVersion()) + ) Logger.info(pkg.getBuildStates(archs)) if can_retry: @@ -278,8 +323,8 @@ def main(): if options.priority and can_rescore: Logger.info(pkg.rescoreBuilds(archs, options.priority)) - Logger.info('') + Logger.info("") -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ubuntu-iso b/ubuntu-iso index 3c5afbb..20c660f 100755 --- a/ubuntu-iso +++ b/ubuntu-iso @@ -25,14 +25,15 @@ import subprocess import sys from ubuntutools import getLogger + Logger = getLogger() def extract(iso, path): - command = ['isoinfo', '-R', '-i', iso, '-x', path] - pipe = subprocess.run(command, encoding='utf-8', - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + command = ["isoinfo", "-R", "-i", iso, "-x", path] + pipe = subprocess.run( + command, encoding="utf-8", stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) if pipe.returncode != 0: sys.stderr.write(pipe.stderr) @@ -42,22 +43,21 @@ def extract(iso, path): def main(): - desc = 'Given an ISO, %prog will display the Ubuntu version information' - parser = optparse.OptionParser(usage='%prog [options] iso...', - description=desc) + desc = "Given an ISO, %prog will display the Ubuntu version information" + parser = optparse.OptionParser(usage="%prog [options] iso...", description=desc) isos = parser.parse_args()[1] err = False for iso in isos: if len(isos) > 1: - prefix = '%s:' % iso + prefix = "%s:" % iso else: - prefix = '' + prefix = "" - version = extract(iso, '/.disk/info') + version = extract(iso, "/.disk/info") if len(version) == 0: - Logger.error('%s does not appear to be an Ubuntu ISO' % iso) + Logger.error("%s does not appear to be an Ubuntu ISO" % iso) err = True continue @@ -67,6 +67,6 @@ def main(): sys.exit(1) -if __name__ == '__main__': +if __name__ == "__main__": main() sys.exit(0) diff --git a/ubuntu-upload-permission b/ubuntu-upload-permission index 394b186..988c445 100755 --- a/ubuntu-upload-permission +++ b/ubuntu-upload-permission @@ -17,28 +17,45 @@ import optparse import sys -from ubuntutools.lp.lpapicache import (Launchpad, Distribution, PersonTeam, - Packageset, PackageNotFoundException, - SeriesNotFoundException) +from ubuntutools.lp.lpapicache import ( + Launchpad, + Distribution, + PersonTeam, + Packageset, + PackageNotFoundException, + SeriesNotFoundException, +) from ubuntutools.misc import split_release_pocket from ubuntutools import getLogger + Logger = getLogger() def parse_arguments(): - '''Parse arguments and return (options, package)''' - parser = optparse.OptionParser('%prog [options] package') - parser.add_option('-r', '--release', default=None, metavar='RELEASE', - help='Use RELEASE, rather than the current development ' - 'release') - parser.add_option('-a', '--list-uploaders', - default=False, action='store_true', - help='List all the people/teams with upload rights') - parser.add_option('-t', '--list-team-members', - default=False, action='store_true', - help='List all team members of teams with upload rights ' - '(implies --list-uploaders)') + """Parse arguments and return (options, package)""" + parser = optparse.OptionParser("%prog [options] package") + parser.add_option( + "-r", + "--release", + default=None, + metavar="RELEASE", + help="Use RELEASE, rather than the current development release", + ) + parser.add_option( + "-a", + "--list-uploaders", + default=False, + action="store_true", + help="List all the people/teams with upload rights", + ) + parser.add_option( + "-t", + "--list-team-members", + default=False, + action="store_true", + help="List all team members of teams with upload rights (implies --list-uploaders)", + ) options, args = parser.parse_args() if len(args) != 1: @@ -52,12 +69,12 @@ def parse_arguments(): def main(): - '''Query upload permissions''' + """Query upload permissions""" options, package = parse_arguments() # Need to be logged in to see uploaders: Launchpad.login() - ubuntu = Distribution('ubuntu') + ubuntu = Distribution("ubuntu") archive = ubuntu.getArchive() if options.release is None: options.release = ubuntu.getDevelopmentSeries().name @@ -74,20 +91,22 @@ def main(): Logger.error(str(e)) sys.exit(2) component = spph.getComponent() - if (options.list_uploaders and (pocket != 'Release' or series.status in - ('Experimental', 'Active Development', 'Pre-release Freeze'))): + if options.list_uploaders and ( + pocket != "Release" + or series.status in ("Experimental", "Active Development", "Pre-release Freeze") + ): - component_uploader = archive.getUploadersForComponent( - component_name=component)[0] + component_uploader = archive.getUploadersForComponent(component_name=component)[0] Logger.info("All upload permissions for %s:" % package) Logger.info("") Logger.info("Component (%s)" % component) Logger.info("============" + ("=" * len(component))) print_uploaders([component_uploader], options.list_team_members) - packagesets = sorted(Packageset.setsIncludingSource( - distroseries=series, - sourcepackagename=package), key=lambda p: p.name) + packagesets = sorted( + Packageset.setsIncludingSource(distroseries=series, sourcepackagename=package), + key=lambda p: p.name, + ) if packagesets: Logger.info("") Logger.info("Packagesets") @@ -95,11 +114,12 @@ def main(): for packageset in packagesets: Logger.info("") Logger.info("%s:" % packageset.name) - print_uploaders(archive.getUploadersForPackageset( - packageset=packageset), options.list_team_members) + print_uploaders( + archive.getUploadersForPackageset(packageset=packageset), + options.list_team_members, + ) - ppu_uploaders = archive.getUploadersForPackage( - source_package_name=package) + ppu_uploaders = archive.getUploadersForPackage(source_package_name=package) if ppu_uploaders: Logger.info("") Logger.info("Per-Package-Uploaders") @@ -108,37 +128,45 @@ def main(): print_uploaders(ppu_uploaders, options.list_team_members) Logger.info("") - if PersonTeam.me.canUploadPackage(archive, series, package, component, - pocket): + if PersonTeam.me.canUploadPackage(archive, series, package, component, pocket): Logger.info("You can upload %s to %s." % (package, options.release)) else: Logger.info("You can not upload %s to %s, yourself." % (package, options.release)) - if (series.status in ('Current Stable Release', 'Supported', 'Obsolete') - and pocket == 'Release'): - Logger.info("%s is in the '%s' state. You may want to query the %s-proposed pocket." % - (release, series.status, release)) + if ( + series.status in ("Current Stable Release", "Supported", "Obsolete") + and pocket == "Release" + ): + Logger.info( + "%s is in the '%s' state. You may want to query the %s-proposed pocket." + % (release, series.status, release) + ) else: - Logger.info("But you can still contribute to it via the sponsorship " - "process: https://wiki.ubuntu.com/SponsorshipProcess") + Logger.info( + "But you can still contribute to it via the sponsorship " + "process: https://wiki.ubuntu.com/SponsorshipProcess" + ) if not options.list_uploaders: - Logger.info("To see who has the necessary upload rights, " - "use the --list-uploaders option.") + Logger.info( + "To see who has the necessary upload rights, " + "use the --list-uploaders option." + ) sys.exit(1) -def print_uploaders(uploaders, expand_teams=False, prefix=''): +def print_uploaders(uploaders, expand_teams=False, prefix=""): """Given a list of uploaders, pretty-print them all Each line is prefixed with prefix. If expand_teams is set, recurse, adding more spaces to prefix on each recursion. """ for uploader in sorted(uploaders, key=lambda p: p.display_name): - Logger.info("%s* %s (%s)%s" % - (prefix, uploader.display_name, uploader.name, - ' [team]' if uploader.is_team else '')) + Logger.info( + "%s* %s (%s)%s" + % (prefix, uploader.display_name, uploader.name, " [team]" if uploader.is_team else "") + ) if expand_teams and uploader.is_team: - print_uploaders(uploader.participants, True, prefix=prefix + ' ') + print_uploaders(uploader.participants, True, prefix=prefix + " ") -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ubuntutools/__init__.py b/ubuntutools/__init__.py index bb7a22d..49f7681 100644 --- a/ubuntutools/__init__.py +++ b/ubuntutools/__init__.py @@ -8,7 +8,7 @@ import sys def getLogger(): - ''' Get the logger instance for this module + """Get the logger instance for this module Quick guide for using this or not: if you want to call ubuntutools module code and have its output print to stdout/stderr ONLY, you can @@ -33,12 +33,12 @@ def getLogger(): This should only be used by runnable scripts provided by the ubuntu-dev-tools package, or other runnable scripts that want the behavior described above. - ''' + """ logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) logger.propagate = False - fmt = logging.Formatter('%(message)s') + fmt = logging.Formatter("%(message)s") stdout_handler = logging.StreamHandler(stream=sys.stdout) stdout_handler.setFormatter(fmt) @@ -47,7 +47,7 @@ def getLogger(): stderr_handler = logging.StreamHandler(stream=sys.stderr) stderr_handler.setFormatter(fmt) - stderr_handler.setLevel(logging.INFO+1) + stderr_handler.setLevel(logging.INFO + 1) logger.addHandler(stderr_handler) return logger diff --git a/ubuntutools/archive.py b/ubuntutools/archive.py index f131efc..bee45d2 100644 --- a/ubuntutools/archive.py +++ b/ubuntutools/archive.py @@ -37,7 +37,7 @@ import subprocess import sys import tempfile -from abc import (ABC, abstractmethod) +from abc import ABC, abstractmethod from debian.changelog import Changelog import debian.deb822 @@ -47,25 +47,32 @@ from contextlib import closing from pathlib import Path from ubuntutools.config import UDTConfig -from ubuntutools.lp.lpapicache import (Launchpad, - Distribution, - PersonTeam, - Project, - SourcePackagePublishingHistory, - HTTPError) -from ubuntutools.lp.udtexceptions import (PackageNotFoundException, - SeriesNotFoundException, - PocketDoesNotExistError, - InvalidDistroValueError) -from ubuntutools.misc import (download, - download_bytes, - verify_file_checksum, - verify_file_checksums, - DownloadError, - NotFoundError) +from ubuntutools.lp.lpapicache import ( + Launchpad, + Distribution, + PersonTeam, + Project, + SourcePackagePublishingHistory, + HTTPError, +) +from ubuntutools.lp.udtexceptions import ( + PackageNotFoundException, + SeriesNotFoundException, + PocketDoesNotExistError, + InvalidDistroValueError, +) +from ubuntutools.misc import ( + download, + download_bytes, + verify_file_checksum, + verify_file_checksums, + DownloadError, + NotFoundError, +) from ubuntutools.version import Version import logging + Logger = logging.getLogger(__name__) @@ -74,17 +81,28 @@ class Dsc(debian.deb822.Dsc): def get_strongest_checksum(self): "Return alg, dict by filename of size, hash_ pairs" - if 'Checksums-Sha256' in self: - return ('sha256', - dict((entry['name'], (int(entry['size']), entry['sha256'])) - for entry in self['Checksums-Sha256'])) - if 'Checksums-Sha1' in self: - return ('sha1', - dict((entry['name'], (int(entry['size']), entry['sha1'])) - for entry in self['Checksums-Sha1'])) - return ('md5', - dict((entry['name'], (int(entry['size']), entry['md5sum'])) - for entry in self['Files'])) + if "Checksums-Sha256" in self: + return ( + "sha256", + dict( + (entry["name"], (int(entry["size"]), entry["sha256"])) + for entry in self["Checksums-Sha256"] + ), + ) + if "Checksums-Sha1" in self: + return ( + "sha1", + dict( + (entry["name"], (int(entry["size"]), entry["sha1"])) + for entry in self["Checksums-Sha1"] + ), + ) + return ( + "md5", + dict( + (entry["name"], (int(entry["size"]), entry["md5sum"])) for entry in self["Files"] + ), + ) def verify_file(self, pathname): "Verify that pathname matches the checksums in the dsc" @@ -98,17 +116,19 @@ class Dsc(debian.deb822.Dsc): def compare_dsc(self, other): """Check whether any files in these two dscs that have the same name also have the same checksum.""" - for field, key in (('Checksums-Sha256', 'sha256'), - ('Checksums-Sha1', 'sha1'), - ('Files', 'md5sum')): + for field, key in ( + ("Checksums-Sha256", "sha256"), + ("Checksums-Sha1", "sha1"), + ("Files", "md5sum"), + ): if field not in self or field not in other: continue - our_checksums = \ - dict((entry['name'], (int(entry['size']), entry[key])) - for entry in self[field]) - their_checksums = \ - dict((entry['name'], (int(entry['size']), entry[key])) - for entry in other[field]) + our_checksums = dict( + (entry["name"], (int(entry["size"]), entry[key])) for entry in self[field] + ) + their_checksums = dict( + (entry["name"], (int(entry["size"]), entry[key])) for entry in other[field] + ) for name, (size, checksum) in our_checksums.items(): if name not in their_checksums: # file only in one dsc @@ -134,8 +154,7 @@ class SourcePackage(ABC): def spph_class(self): return SourcePackagePublishingHistory - def __init__(self, package=None, version=None, component=None, - *args, **kwargs): + def __init__(self, package=None, version=None, component=None, *args, **kwargs): """Can be initialised using either package or dscfile. If package is specified, either the version or series can also be specified; using version will get the specific package version, @@ -143,28 +162,28 @@ class SourcePackage(ABC): Specifying only the package with no version or series will get the latest version from the development series. """ - dscfile = kwargs.get('dscfile') - mirrors = kwargs.get('mirrors', ()) - workdir = kwargs.get('workdir') - series = kwargs.get('series') - pocket = kwargs.get('pocket') - status = kwargs.get('status') - verify_signature = kwargs.get('verify_signature', False) - try_binary = kwargs.get('try_binary', True) + dscfile = kwargs.get("dscfile") + mirrors = kwargs.get("mirrors", ()) + workdir = kwargs.get("workdir") + series = kwargs.get("series") + pocket = kwargs.get("pocket") + status = kwargs.get("status") + verify_signature = kwargs.get("verify_signature", False) + try_binary = kwargs.get("try_binary", True) - assert (package is not None or dscfile is not None) + assert package is not None or dscfile is not None - if 'lp' in kwargs: + if "lp" in kwargs: # deprecated - please don't use this; if you want to use an # existing lp object, just call login_existing() directly Logger.warning("Deprecation warning: please don't pass 'lp' to SourcePackage") if not Launchpad.logged_in: - Launchpad.login_existing(kwargs['lp']) + Launchpad.login_existing(kwargs["lp"]) self.source = package self.binary = None self.try_binary = try_binary - self.workdir = Path(workdir) if workdir else Path('.') + self.workdir = Path(workdir) if workdir else Path(".") self._series = series self._pocket = pocket self._status = status @@ -182,15 +201,21 @@ class SourcePackage(ABC): # Mirrors self.mirrors = list(filter(None, mirrors)) - self.masters = list(filter(None, - [UDTConfig.defaults.get(f'{self.distribution.upper()}_{suffix}') - for suffix in ["MIRROR", "PORTS_MIRROR", "INTERNAL_MIRROR"]])) + self.masters = list( + filter( + None, + [ + UDTConfig.defaults.get(f"{self.distribution.upper()}_{suffix}") + for suffix in ["MIRROR", "PORTS_MIRROR", "INTERNAL_MIRROR"] + ], + ) + ) # If provided a dscfile, process it now to set our source and version if self._dsc_source: self._dsc = Dsc(download_bytes(self._dsc_source)) - self.source = self._dsc['Source'] - self._version = Version(self._dsc['Version']) + self.source = self._dsc["Source"] + self._version = Version(self._dsc["Version"]) self._check_dsc_signature() @property @@ -203,23 +228,21 @@ class SourcePackage(ABC): params = {} if self._version: # if version was specified, use that - params['version'] = self._version.full_version + params["version"] = self._version.full_version elif self._series: # if version not specified, get the latest from this series - params['series'] = self._series + params["series"] = self._series # note that if not specified, pocket defaults to all EXCEPT -backports if self._pocket: - params['pocket'] = self._pocket + params["pocket"] = self._pocket else: # We always want to search all series, if not specified - params['search_all_series'] = True + params["search_all_series"] = True - params['status'] = self._status + params["status"] = self._status try: - self._spph = archive.getSourcePackage(self.source, - wrapper=self.spph_class, - **params) + self._spph = archive.getSourcePackage(self.source, wrapper=self.spph_class, **params) return self._spph except PackageNotFoundException as pnfe: if not self.try_binary or self.binary: @@ -227,8 +250,9 @@ class SourcePackage(ABC): # or we've already tried raise pnfe - Logger.info('Source package lookup failed, ' - 'trying lookup of binary package %s' % self.source) + Logger.info( + "Source package lookup failed, trying lookup of binary package %s" % self.source + ) try: bpph = archive.getBinaryPackage(self.source, **params) @@ -240,8 +264,11 @@ class SourcePackage(ABC): self.binary = self.source self.source = bpph.getSourcePackageName() - Logger.info("Using source package '{}' for binary package '{}'" - .format(self.source, self.binary)) + Logger.info( + "Using source package '{}' for binary package '{}'".format( + self.source, self.binary + ) + ) spph = bpph.getBuild().getSourcePackagePublishingHistory() if spph: @@ -268,14 +295,14 @@ class SourcePackage(ABC): def component(self): "Cached archive component, in available" if not self._component: - Logger.debug('Determining component from Launchpad') + Logger.debug("Determining component from Launchpad") self._component = self.lp_spph.getComponent() return self._component @property def dsc_name(self): "Return the source package dsc filename for the given package" - return f'{self.source}_{self.version.strip_epoch()}.dsc' + return f"{self.source}_{self.version.strip_epoch()}.dsc" @property def dsc_pathname(self): @@ -287,7 +314,7 @@ class SourcePackage(ABC): "Return the Dsc" if not self._dsc: if self._dsc_source: - raise RuntimeError('Internal error: we have a dsc file but dsc not set') + raise RuntimeError("Internal error: we have a dsc file but dsc not set") urls = self._source_urls(self.dsc_name) with tempfile.TemporaryDirectory() as d: tmpdsc = Path(d) / self.dsc_name @@ -306,12 +333,11 @@ class SourcePackage(ABC): def _mirror_url(self, mirror, component, filename): "Build a source package URL on a mirror" - if self.source.startswith('lib'): + if self.source.startswith("lib"): group = self.source[:4] else: group = self.source[0] - return os.path.join(mirror, 'pool', component, group, - self.source, filename) + return os.path.join(mirror, "pool", component, group, self.source, filename) def _archive_servers(self): "Generator for mirror and master servers" @@ -350,28 +376,33 @@ class SourcePackage(ABC): if not self._verify_signature: return try: - gpg_info = self.dsc.get_gpg_info(( - '/usr/share/keyrings/debian-keyring.gpg', - '/usr/share/keyrings/debian-maintainers.gpg', - )) + gpg_info = self.dsc.get_gpg_info( + ( + "/usr/share/keyrings/debian-keyring.gpg", + "/usr/share/keyrings/debian-maintainers.gpg", + ) + ) except IOError: - Logger.debug('Signature on %s could not be verified, install ' - 'debian-keyring' % self.dsc_name) + Logger.debug( + "Signature on %s could not be verified, install debian-keyring" % self.dsc_name + ) return if gpg_info.valid(): - if 'GOODSIG' in gpg_info: - Logger.info('Good signature by %s (0x%s)' - % (gpg_info['GOODSIG'][1], gpg_info['GOODSIG'][0])) - elif 'VALIDSIG' in gpg_info: - Logger.info('Valid signature by 0x%s' % gpg_info['VALIDSIG'][0]) + if "GOODSIG" in gpg_info: + Logger.info( + "Good signature by %s (0x%s)" + % (gpg_info["GOODSIG"][1], gpg_info["GOODSIG"][0]) + ) + elif "VALIDSIG" in gpg_info: + Logger.info("Valid signature by 0x%s" % gpg_info["VALIDSIG"][0]) else: - Logger.info('Valid signature') - elif 'NO_PUBKEY' in gpg_info: - Logger.warning('Public key not found, could not verify signature') - elif 'NODATA' in gpg_info: - Logger.warning('Package is not signed') + Logger.info("Valid signature") + elif "NO_PUBKEY" in gpg_info: + Logger.warning("Public key not found, could not verify signature") + elif "NODATA" in gpg_info: + Logger.warning("Package is not signed") else: - Logger.warning('Signature on %s could not be verified' % self.dsc_name) + Logger.warning("Signature on %s could not be verified" % self.dsc_name) def _verify_file(self, pathname, dscverify=False, sha1sum=None, sha256sum=None, size=0): p = Path(pathname) @@ -381,9 +412,9 @@ class SourcePackage(ABC): return False checksums = {} if sha1sum: - checksums['SHA1'] = sha1sum + checksums["SHA1"] = sha1sum if sha256sum: - checksums['SHA256'] = sha256sum + checksums["SHA256"] = sha256sum if not verify_file_checksums(p, checksums, size): return False return True @@ -397,30 +428,32 @@ class SourcePackage(ABC): can_verify = any((dscverify, sha1sum, sha256sum)) if can_verify and self._verify_file(p, dscverify, sha1sum, sha256sum, size): - Logger.info(f'Using existing file {p}') + Logger.info(f"Using existing file {p}") return True download(url, p, size) return self._verify_file(p, dscverify, sha1sum, sha256sum, size) - def _download_file_from_urls(self, urls, filename, size=0, dscverify=False, - sha1sum=None, sha256sum=None): + def _download_file_from_urls( + self, urls, filename, size=0, dscverify=False, sha1sum=None, sha256sum=None + ): "Try to download a file from a list of urls." for url in urls: try: - if self._download_file(url, filename, size, dscverify=dscverify, - sha1sum=sha1sum, sha256sum=sha256sum): + if self._download_file( + url, filename, size, dscverify=dscverify, sha1sum=sha1sum, sha256sum=sha256sum + ): return except NotFoundError: # It's ok if the file isn't found, just try the next url - Logger.debug(f'File not found at {url}') + Logger.debug(f"File not found at {url}") except DownloadError as e: - Logger.error(f'Download Error: {e}') - raise DownloadError(f'Failed to download {filename}') + Logger.error(f"Download Error: {e}") + raise DownloadError(f"Failed to download {filename}") def pull_dsc(self): - '''DEPRECATED + """DEPRECATED This method is badly named and historically has only 'pulled' the dsc into memory, not actually to a file. Since the other 'pull' methods @@ -430,16 +463,16 @@ class SourcePackage(ABC): This method no longer does anything at all and should not be used by anyone. - ''' + """ Logger.debug('Please fix your program: the "pull_dsc" method is deprecated') def pull(self): "Pull into workdir" Path(self.dsc_pathname).write_bytes(self.dsc.raw_text) - for entry in self.dsc['Files']: - name = entry['name'] + for entry in self.dsc["Files"]: + name = entry["name"] urls = self._source_urls(name) - self._download_file_from_urls(urls, name, int(entry['size']), dscverify=True) + self._download_file_from_urls(urls, name, int(entry["size"]), dscverify=True) def pull_binaries(self, arch=None, name=None, ext=None): """Pull binary debs into workdir. @@ -454,7 +487,7 @@ class SourcePackage(ABC): """ Logger.debug("pull_binaries(arch=%s, name=%s, ext=%s)" % (arch, name, ext)) - if arch == 'all': + if arch == "all": arch = None total = 0 @@ -465,8 +498,7 @@ class SourcePackage(ABC): fsize = bpph.binaryFileSize(fname) urls = self._binary_urls(fname, bpph) try: - self._download_file_from_urls(urls, fname, fsize, - sha1sum=fsha1, sha256sum=fsha256) + self._download_file_from_urls(urls, fname, fsize, sha1sum=fsha1, sha256sum=fsha256) total += 1 except DownloadError as e: # log/print the error, but continue to get the rest of the files @@ -477,28 +509,36 @@ class SourcePackage(ABC): """Verify that the source package in workdir matches the dsc. Return boolean """ - return all(self.dsc.verify_file(self.workdir / entry['name']) - for entry in self.dsc['Files']) + return all( + self.dsc.verify_file(self.workdir / entry["name"]) for entry in self.dsc["Files"] + ) def verify_orig(self): """Verify that the .orig files in workdir match the dsc. Return boolean """ - orig_re = re.compile(r'.*\.orig(-[^.]+)?\.tar\.[^.]+$') - return all(self.dsc.verify_file(self.workdir / entry['name']) - for entry in self.dsc['Files'] - if orig_re.match(entry['name'])) + orig_re = re.compile(r".*\.orig(-[^.]+)?\.tar\.[^.]+$") + return all( + self.dsc.verify_file(self.workdir / entry["name"]) + for entry in self.dsc["Files"] + if orig_re.match(entry["name"]) + ) def unpack(self, destdir=None): "Unpack in workdir" - cmd = ['dpkg-source', '-x', self.dsc_name] + cmd = ["dpkg-source", "-x", self.dsc_name] if destdir: cmd.append(destdir) - Logger.debug(' '.join(cmd)) - result = subprocess.run(cmd, cwd=str(self.workdir), encoding='utf-8', - stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + Logger.debug(" ".join(cmd)) + result = subprocess.run( + cmd, + cwd=str(self.workdir), + encoding="utf-8", + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) if result.returncode != 0: - Logger.error('Source unpack failed.') + Logger.error("Source unpack failed.") Logger.debug(result.stdout) def debdiff(self, newpkg, diffstat=False): @@ -506,18 +546,18 @@ class SourcePackage(ABC): Optionally print diffstat. Return the debdiff filename. """ - cmd = ['debdiff', self.dsc_name, newpkg.dsc_name] - difffn = newpkg.dsc_name[:-3] + 'debdiff' - Logger.debug(' '.join(cmd) + ('> %s' % difffn)) - with open(difffn, 'w') as f: + cmd = ["debdiff", self.dsc_name, newpkg.dsc_name] + difffn = newpkg.dsc_name[:-3] + "debdiff" + Logger.debug(" ".join(cmd) + ("> %s" % difffn)) + with open(difffn, "w") as f: if subprocess.call(cmd, stdout=f, cwd=str(self.workdir)) > 2: - Logger.error('Debdiff failed.') + Logger.error("Debdiff failed.") sys.exit(1) if diffstat: - cmd = ('diffstat', '-p1', difffn) - Logger.debug(' '.join(cmd)) + cmd = ("diffstat", "-p1", difffn) + Logger.debug(" ".join(cmd)) if subprocess.call(cmd): - Logger.error('diffstat failed.') + Logger.error("diffstat failed.") sys.exit(1) return os.path.abspath(difffn) @@ -526,7 +566,8 @@ class DebianSPPH(SourcePackagePublishingHistory): """SPPH with getBinaries() overridden, as LP doesn't have Debian binaries """ - resource_type = 'source_package_publishing_history' + + resource_type = "source_package_publishing_history" def __init__(self, *args, **kwargs): super(DebianSPPH, self).__init__(*args, **kwargs) @@ -534,9 +575,10 @@ class DebianSPPH(SourcePackagePublishingHistory): def getBinaries(self, arch=None, name=None, ext=None): if not self._srcpkg: - Logger.info('Using Snapshot to find binary packages') - self._srcpkg = Snapshot.getSourcePackage(self.getPackageName(), - version=self.getVersion()) + Logger.info("Using Snapshot to find binary packages") + self._srcpkg = Snapshot.getSourcePackage( + self.getPackageName(), version=self.getVersion() + ) return self._srcpkg.getSPPH().getBinaries(arch=arch, name=name, ext=ext) @@ -545,7 +587,7 @@ class DebianSourcePackage(SourcePackage): @property def distribution(self): - return 'debian' + return "debian" @property def spph_class(self): @@ -553,7 +595,7 @@ class DebianSourcePackage(SourcePackage): def __init__(self, *args, **kwargs): super(DebianSourcePackage, self).__init__(*args, **kwargs) - self.masters.append(UDTConfig.defaults['DEBSEC_MIRROR']) + self.masters.append(UDTConfig.defaults["DEBSEC_MIRROR"]) # Cached values: self._snapshot_package = None @@ -563,9 +605,8 @@ class DebianSourcePackage(SourcePackage): # Debian doesn't have 'pockets' if self._pocket: - if self._pocket.lower() != 'release': - Logger.error("Debian does not use 'pockets', ignoring pocket '%s'", - self._pocket) + if self._pocket.lower() != "release": + Logger.error("Debian does not use 'pockets', ignoring pocket '%s'", self._pocket) self._pocket = None # Overridden properties/methods: @@ -581,7 +622,7 @@ class DebianSourcePackage(SourcePackage): except SeriesNotFoundException: pass - Logger.info('Package not found in Launchpad, using Snapshot') + Logger.info("Package not found in Launchpad, using Snapshot") self._spph = self.snapshot_package.getSPPH() return self._spph @@ -589,7 +630,7 @@ class DebianSourcePackage(SourcePackage): def component(self): "Cached archive component, in available" if not self._component: - Logger.debug('Determining component from Snapshot') + Logger.debug("Determining component from Snapshot") self._component = Snapshot.getComponent(self.source, self.version) return self._component @@ -615,9 +656,9 @@ class DebianSourcePackage(SourcePackage): self._snapshot_package = srcpkg else: # we have neither version nor spph, so look up our version using madison - Logger.info('Using madison to find latest version number') + Logger.info("Using madison to find latest version number") series = self._series - params = {'series': series} if series else {} + params = {"series": series} if series else {} srcpkg = Madison(self.distribution).getSourcePackage(self.source, **params) if not srcpkg: raise PackageNotFoundException("Package {} not found".format(self.source)) @@ -640,18 +681,18 @@ class UbuntuSourcePackage(SourcePackage): @property def distribution(self): - return 'ubuntu' + return "ubuntu" class PersonalPackageArchiveSourcePackage(UbuntuSourcePackage): "Download / unpack an Ubuntu Personal Package Archive source package" + def __init__(self, *args, **kwargs): super(PersonalPackageArchiveSourcePackage, self).__init__(*args, **kwargs) - assert 'ppa' in kwargs - ppa = kwargs['ppa'].split('/') + assert "ppa" in kwargs + ppa = kwargs["ppa"].split("/") if len(ppa) != 2: - raise ValueError('Invalid PPA value "%s",' - 'must be "/"' % kwargs['ppa']) + raise ValueError('Invalid PPA value "%s",' 'must be "/"' % kwargs["ppa"]) self._teamname = ppa[0] self._ppaname = ppa[1] self.masters = [] @@ -674,8 +715,8 @@ class PersonalPackageArchiveSourcePackage(UbuntuSourcePackage): "Format the URL for a filename using the private PPA server" url = self.getArchive().getMySubscriptionURL() pkg = self.source - subdir = pkg[:4] if pkg.startswith('lib') else pkg[:1] - return f'{url}/pool/main/{subdir}/{pkg}/{filename}' + subdir = pkg[:4] if pkg.startswith("lib") else pkg[:1] + return f"{url}/pool/main/{subdir}/{pkg}/{filename}" def _source_urls(self, name): "Generator of sources for name" @@ -694,28 +735,32 @@ class PersonalPackageArchiveSourcePackage(UbuntuSourcePackage): class UbuntuCloudArchiveSourcePackage(PersonalPackageArchiveSourcePackage): "Download / unpack an Ubuntu Cloud Archive source package" - TEAM = 'ubuntu-cloud-archive' - PROJECT = 'cloud-archive' - VALID_POCKETS = ['updates', 'proposed', 'staging'] + TEAM = "ubuntu-cloud-archive" + PROJECT = "cloud-archive" + VALID_POCKETS = ["updates", "proposed", "staging"] def __init__(self, *args, **kwargs): # Need to determine actual series/pocket ppa now, as it affects getArchive() - (series, pocket) = self._findReleaseAndPocketForPackage(kwargs.get('series'), - kwargs.get('pocket'), - kwargs.get('package'), - kwargs.get('version')) + (series, pocket) = self._findReleaseAndPocketForPackage( + kwargs.get("series"), + kwargs.get("pocket"), + kwargs.get("package"), + kwargs.get("version"), + ) # Drop series/pocket from kwargs, as UCA handles them completely different and we # don't want to pass them up to the superclass - kwargs.pop('series', None) - orig_pocket = kwargs.pop('pocket', None) - if orig_pocket and orig_pocket != pocket and pocket == 'staging': - Logger.info(f"Ubuntu Cloud Archive release '{series}' pocket '{orig_pocket}'" - " PPA is not public, using 'staging' pocket instead") + kwargs.pop("series", None) + orig_pocket = kwargs.pop("pocket", None) + if orig_pocket and orig_pocket != pocket and pocket == "staging": + Logger.info( + f"Ubuntu Cloud Archive release '{series}' pocket '{orig_pocket}'" + " PPA is not public, using 'staging' pocket instead" + ) - kwargs['ppa'] = f"{self.TEAM}/{series}-{pocket}" + kwargs["ppa"] = f"{self.TEAM}/{series}-{pocket}" super(UbuntuCloudArchiveSourcePackage, self).__init__(*args, **kwargs) - if pocket == 'staging': + if pocket == "staging": # Don't bother with the archive; just get directly from the staging ppa, since # none of the binaries from the archive will match the staging checksums self.masters = [] @@ -729,9 +774,11 @@ class UbuntuCloudArchiveSourcePackage(PersonalPackageArchiveSourcePackage): pulling binaries when using a 'staging' ppa, since the published binaries will not match the 'staging' binaries. """ - if self._ppaname.endswith('-staging'): - Logger.warning("Binaries from 'staging' pocket will not match published binaries; " - "see https://bugs.launchpad.net/cloud-archive/+bug/1649979") + if self._ppaname.endswith("-staging"): + Logger.warning( + "Binaries from 'staging' pocket will not match published binaries; " + "see https://bugs.launchpad.net/cloud-archive/+bug/1649979" + ) return super(UbuntuCloudArchiveSourcePackage, self).pull_binaries(arch, name, ext) @classmethod @@ -769,7 +816,7 @@ class UbuntuCloudArchiveSourcePackage(PersonalPackageArchiveSourcePackage): @classmethod @functools.lru_cache() def getUbuntuCloudArchivePPAs(cls, release=None, pocket=None): - """ Get sorted list of UCA ppa Archive objects + """Get sorted list of UCA ppa Archive objects If release and/or pocket are specified, the list will be filtered to return only matching ppa(s). @@ -790,12 +837,12 @@ class UbuntuCloudArchiveSourcePackage(PersonalPackageArchiveSourcePackage): # Use recursive call without params to get the lru-cached list of ppas returned above ppas = cls.getUbuntuCloudArchivePPAs() if release: - ppas = list(filter(lambda p: p.name.partition('-')[0] == release, ppas)) + ppas = list(filter(lambda p: p.name.partition("-")[0] == release, ppas)) if pocket: - ppas = list(filter(lambda p: p.name.partition('-')[2] == pocket, ppas)) + ppas = list(filter(lambda p: p.name.partition("-")[2] == pocket, ppas)) if not ppas: - rname = release or '*' - pname = pocket or '*' + rname = release or "*" + pname = pocket or "*" raise SeriesNotFoundException(f"UCA release '{rname}-{pname}' not found") return ppas @@ -825,23 +872,25 @@ class UbuntuCloudArchiveSourcePackage(PersonalPackageArchiveSourcePackage): release = release.lower().strip() # Cases 1 and 2 - PATTERN1 = r'^(?P[a-z]+)(?:-(?P[a-z]+))?$' + PATTERN1 = r"^(?P[a-z]+)(?:-(?P[a-z]+))?$" # Cases 3 and 4 - PATTERN2 = r'^(?P[a-z]+)-(?P[a-z]+)(?:-(?P[a-z]+))?$' + PATTERN2 = r"^(?P[a-z]+)-(?P[a-z]+)(?:-(?P[a-z]+))?$" # Case 5 - PATTERN3 = r'^(?P[a-z]+)-(?P[a-z]+)/(?P[a-z]+)$' + PATTERN3 = r"^(?P[a-z]+)-(?P[a-z]+)/(?P[a-z]+)$" for pattern in [PATTERN1, PATTERN2, PATTERN3]: match = re.match(pattern, release) if match: - r = match.group('ucarelease') - p = match.group('pocket') + r = match.group("ucarelease") + p = match.group("pocket") # For UCA, there is no 'release' pocket, the default is 'updates' - if p and p == 'release': - Logger.warning("Ubuntu Cloud Archive does not use 'release' pocket," - " using 'updates' instead") - p = 'updates' - if (cls.isValidRelease(r) and (not p or p in cls.VALID_POCKETS)): + if p and p == "release": + Logger.warning( + "Ubuntu Cloud Archive does not use 'release' pocket," + " using 'updates' instead" + ) + p = "updates" + if cls.isValidRelease(r) and (not p or p in cls.VALID_POCKETS): Logger.debug(f"Using Ubuntu Cloud Archive release '{r}'") return (r, p) raise SeriesNotFoundException(f"Ubuntu Cloud Archive release '{release}' not found") @@ -852,25 +901,28 @@ class UbuntuCloudArchiveSourcePackage(PersonalPackageArchiveSourcePackage): raise SeriesNotFoundException(f"Ubuntu Cloud Archive release '{release}' not found") if pocket and pocket not in cls.VALID_POCKETS: raise PocketDoesNotExistError(f"Ubuntu Cloud Archive pocket '{pocket}' is invalid") - DEFAULT = tuple(cls.getUbuntuCloudArchivePPAs(release=release or cls.getDevelSeries())[0] - .name.split('-', maxsplit=1)) + DEFAULT = tuple( + cls.getUbuntuCloudArchivePPAs(release=release or cls.getDevelSeries())[0].name.split( + "-", maxsplit=1 + ) + ) if not package: # not much we can do without a package name return DEFAULT checked_pocket = False for ppa in cls.getUbuntuCloudArchivePPAs(release=release): - if pocket and pocket != ppa.name.partition('-')[2]: + if pocket and pocket != ppa.name.partition("-")[2]: # If pocket is specified, only look at the requested pocket, or 'later' # This allows using the 'staging' pocket for old releases that do not # provide any 'updates' or 'proposed' pockets if not checked_pocket: continue checked_pocket = True - params = {'exact_match': True, 'source_name': package} + params = {"exact_match": True, "source_name": package} if version: - params['version'] = version + params["version"] = version if ppa.getPublishedSources(**params): - (r, _, p) = ppa.name.partition('-') + (r, _, p) = ppa.name.partition("-") return (r, p) # package/version not found in any ppa return DEFAULT @@ -880,8 +932,8 @@ class _WebJSON(object): def getHostUrl(self): raise Exception("Not implemented") - def load(self, path=''): - reader = codecs.getreader('utf-8') + def load(self, path=""): + reader = codecs.getreader("utf-8") url = self.getHostUrl() + path Logger.debug("Loading %s" % url) with closing(urlopen(url)) as data: @@ -895,21 +947,21 @@ class _WebJSON(object): # any details at all for older-than-latest package versions. class Madison(_WebJSON): urls = { - 'debian': 'https://api.ftp-master.debian.org/madison', - 'ubuntu': 'http://people.canonical.com/~ubuntu-archive/madison.cgi', + "debian": "https://api.ftp-master.debian.org/madison", + "ubuntu": "http://people.canonical.com/~ubuntu-archive/madison.cgi", } - def __init__(self, distro='debian'): + def __init__(self, distro="debian"): super(Madison, self).__init__() self._distro = distro # This currently will NOT work with ubuntu; it doesn't support f=json - if distro != 'debian': + if distro != "debian": raise InvalidDistroValueError("Madison currently only supports Debian") def getHostUrl(self): return self.urls[self._distro] - def getSourcePackage(self, name, series='unstable'): + def getSourcePackage(self, name, series="unstable"): url = "?f=json&package={name}&s={series}".format(name=name, series=series) try: result = self.load(url) @@ -920,8 +972,7 @@ class Madison(_WebJSON): raise PackageNotFoundException(msg) versions = list(result[0][name].values())[0] latest = versions[sorted(versions.keys(), reverse=True)[0]] - return Snapshot.getSourcePackage(name=latest['source'], - version=latest['source_version']) + return Snapshot.getSourcePackage(name=latest["source"], version=latest["source_version"]) # Snapshot API @@ -942,20 +993,20 @@ class _Snapshot(_WebJSON): except HTTPError: msg = "Package {} version {} not found" raise PackageNotFoundException(msg.format(name, version)) - result = response.get('result') - info = response.get('fileinfo') + result = response.get("result") + info = response.get("fileinfo") if len(result) < 1: msg = "No source files for package {} version {}" raise PackageNotFoundException(msg.format(name, version)) - path = info[result[0]['hash']][0]['path'] + path = info[result[0]["hash"]][0]["path"] # this expects the 'component' to follow 'pool[-*]' in the path found_pool = False component = None - for s in path.split('/'): + for s in path.split("/"): if found_pool: component = s break - if s.startswith('pool'): + if s.startswith("pool"): found_pool = True if not component: Logger.warning("could not determine component from path %s" % path) @@ -966,29 +1017,29 @@ class _Snapshot(_WebJSON): def _get_package(self, name, url, pkginit, version, sort_key): try: - results = self.load("/mr/{}/{}/".format(url, name))['result'] + results = self.load("/mr/{}/{}/".format(url, name))["result"] except HTTPError: raise PackageNotFoundException("Package {} not found.".format(name)) results = sorted(results, key=lambda r: r[sort_key], reverse=True) - results = [pkginit(r) for r in results if version == r['version']] + results = [pkginit(r) for r in results if version == r["version"]] if not results: msg = "Package {name} version {version} not found." raise PackageNotFoundException(msg.format(name=name, version=version)) return results def getSourcePackages(self, name, version): - return self._get_package(name, "package", - lambda obj: SnapshotSourcePackage(obj, name), - version, "version") + return self._get_package( + name, "package", lambda obj: SnapshotSourcePackage(obj, name), version, "version" + ) def getSourcePackage(self, name, version): return self.getSourcePackages(name, version)[0] def getBinaryPackages(self, name, version): - return self._get_package(name, "binary", - lambda obj: SnapshotBinaryPackage(obj), - version, "binary_version") + return self._get_package( + name, "binary", lambda obj: SnapshotBinaryPackage(obj), version, "binary_version" + ) def getBinaryPackage(self, name, version): return self.getBinaryPackages(name, version)[0] @@ -1005,7 +1056,7 @@ class SnapshotPackage(object): @property def version(self): - return self._obj['version'] + return self._obj["version"] @property def component(self): @@ -1034,11 +1085,20 @@ class SnapshotSourcePackage(SnapshotPackage): if not self._binary_files: url = "/mr/package/{}/{}/allfiles".format(self.name, self.version) response = Snapshot.load("{}?fileinfo=1".format(url)) - info = response['fileinfo'] - files = [SnapshotBinaryFile(b['name'], b['version'], self.component, - info[r['hash']][0], r['hash'], - r['architecture'], self.name) - for b in response['result']['binaries'] for r in b['files']] + info = response["fileinfo"] + files = [ + SnapshotBinaryFile( + b["name"], + b["version"], + self.component, + info[r["hash"]][0], + r["hash"], + r["architecture"], + self.name, + ) + for b in response["result"]["binaries"] + for r in b["files"] + ] self._binary_files = files bins = list(self._binary_files) if arch: @@ -1053,10 +1113,13 @@ class SnapshotSourcePackage(SnapshotPackage): if not self._files: url = "/mr/package/{}/{}/srcfiles".format(self.name, self.version) response = Snapshot.load("{}?fileinfo=1".format(url)) - info = response['fileinfo'] - self._files = [SnapshotSourceFile(self.name, self.version, self.component, - info[r['hash']][0], r['hash']) - for r in response['result']] + info = response["fileinfo"] + self._files = [ + SnapshotSourceFile( + self.name, self.version, self.component, info[r["hash"]][0], r["hash"] + ) + for r in response["result"] + ] return list(self._files) @@ -1067,15 +1130,15 @@ class SnapshotBinaryPackage(SnapshotPackage): @property def name(self): - return self._obj['name'] + return self._obj["name"] @property def binary_version(self): - return self._obj['binary_version'] + return self._obj["binary_version"] @property def source(self): - return self._obj['source'] + return self._obj["source"] def getBPPH(self, arch): f = self.getFiles(arch) @@ -1090,11 +1153,19 @@ class SnapshotBinaryPackage(SnapshotPackage): if not self._files: url = "/mr/binary/{}/{}/binfiles".format(self.name, self.version) response = Snapshot.load("{}?fileinfo=1".format(url)) - info = response['fileinfo'] - self._files = [SnapshotBinaryFile(self.name, self.version, self.component, - info[r['hash']][0], r['hash'], - r['architecture'], self.source) - for r in response['result']] + info = response["fileinfo"] + self._files = [ + SnapshotBinaryFile( + self.name, + self.version, + self.component, + info[r["hash"]][0], + r["hash"], + r["architecture"], + self.source, + ) + for r in response["result"] + ] if not arch: return list(self._files) return [f for f in self._files if f.isArch(arch)] @@ -1114,33 +1185,33 @@ class SnapshotFile(object): @property def archive_name(self): - return self._obj['archive_name'] + return self._obj["archive_name"] @property def name(self): - return self._obj['name'] + return self._obj["name"] @property def ext(self): - return self.name.rpartition('.')[2] + return self.name.rpartition(".")[2] @property def path(self): - return self._obj['path'] + return self._obj["path"] @property def size(self): - return int(self._obj['size']) + return int(self._obj["size"]) @property def date(self): - if 'run' in self._obj: - return self._obj['run'] - elif 'first_seen' in self._obj: - return self._obj['first_seen'] + if "run" in self._obj: + return self._obj["run"] + elif "first_seen" in self._obj: + return self._obj["first_seen"] else: - Logger.error('File {} has no date information', self.name) - return 'unknown' + Logger.error("File {} has no date information", self.name) + return "unknown" def getHash(self): return self._hash @@ -1157,7 +1228,7 @@ class SnapshotSourceFile(SnapshotFile): super(SnapshotSourceFile, self).__init__(name, version, component, obj, h) def getType(self): - return 'source' + return "source" class SnapshotBinaryFile(SnapshotFile): @@ -1170,12 +1241,12 @@ class SnapshotBinaryFile(SnapshotFile): def isArch(self, arch): if not arch: return True - if self.arch == 'all': + if self.arch == "all": return True return arch == self.arch def getType(self): - return 'binary' + return "binary" def getBPPH(self): if not self._bpph: @@ -1185,6 +1256,7 @@ class SnapshotBinaryFile(SnapshotFile): class SnapshotSPPH(object): """Provide the same interface as SourcePackagePublishingHistory""" + def __init__(self, snapshot_pkg): self._pkg = snapshot_pkg @@ -1196,14 +1268,12 @@ class SnapshotSPPH(object): @property def display_name(self): - return ("{name} {version}" - .format(name=self.getPackageName(), - version=self.getVersion())) + return "{name} {version}".format(name=self.getPackageName(), version=self.getVersion()) @property def pocket(self): # Debian does not use 'pockets' - return 'Release' + return "Release" @property def source_package_name(self): @@ -1226,24 +1296,28 @@ class SnapshotSPPH(object): def sourceFileUrls(self, include_meta=False): if include_meta: - return [{'url': f.getUrl(), - 'filename': f.name, - 'sha1': f.getHash(), - 'sha256': None, - 'size': f.size} - for f in self._pkg.getFiles()] + return [ + { + "url": f.getUrl(), + "filename": f.name, + "sha1": f.getHash(), + "sha256": None, + "size": f.size, + } + for f in self._pkg.getFiles() + ] return [f.getUrl() for f in self._pkg.getFiles()] def sourceFileUrl(self, filename): for f in self.sourceFileUrls(include_meta=True): - if filename == f['filename']: - return f['url'] + if filename == f["filename"]: + return f["url"] return None def sourceFileSha1(self, url_or_filename): for f in self.sourceFileUrls(include_meta=True): - if url_or_filename in [f['url'], f['filename']]: - return f['sha1'] + if url_or_filename in [f["url"], f["filename"]]: + return f["sha1"] return None def sourceFileSha256(self, url_or_filename): @@ -1251,33 +1325,39 @@ class SnapshotSPPH(object): def sourceFileSize(self, url_or_filename): for f in self.sourceFileUrls(include_meta=True): - if url_or_filename in [f['url'], f['filename']]: - return int(f['size']) + if url_or_filename in [f["url"], f["filename"]]: + return int(f["size"]) return 0 def getChangelog(self, since_version=None): - ''' + """ Return the changelog, optionally since a particular version May return None if the changelog isn't available - ''' + """ if self._changelog is None: name = self.getPackageName() - if name.startswith('lib'): - subdir = 'lib%s' % name[3] + if name.startswith("lib"): + subdir = "lib%s" % name[3] else: subdir = name[0] pkgversion = Version(self.getVersion()).strip_epoch() - base = 'http://packages.debian.org/' + base = "http://packages.debian.org/" - url = os.path.join(base, 'changelogs', 'pool', - self.getComponent(), subdir, name, - name + '_' + pkgversion, - 'changelog.txt') + url = os.path.join( + base, + "changelogs", + "pool", + self.getComponent(), + subdir, + name, + name + "_" + pkgversion, + "changelog.txt", + ) try: with closing(urlopen(url)) as f: self._changelog = f.read() except HTTPError as error: - Logger.error('{}: {}'.format(url, error)) + Logger.error("{}: {}".format(url, error)) return None if since_version is None: @@ -1291,22 +1371,22 @@ class SnapshotSPPH(object): if block.version <= since_version: break new_entries.append(str(block)) - return ''.join(new_entries) + return "".join(new_entries) def getBinaries(self, arch=None, name=None, ext=None): - return [b.getBPPH() - for b in self._pkg.getBinaryFiles(arch=arch, name=name, ext=ext)] + return [b.getBPPH() for b in self._pkg.getBinaryFiles(arch=arch, name=name, ext=ext)] class SnapshotBPPH(object): """Provide the same interface as BinaryPackagePublishingHistory""" + def __init__(self, snapshot_binfile): self._file = snapshot_binfile # LP API defined fields @property def architecture_specific(self): - return self._file.arch != 'all' + return self._file.arch != "all" @property def binary_package_name(self): @@ -1322,14 +1402,12 @@ class SnapshotBPPH(object): @property def display_name(self): - return ("{name} {version}" - .format(name=self.getPackageName(), - version=self.getVersion())) + return "{name} {version}".format(name=self.getPackageName(), version=self.getVersion()) @property def pocket(self): # Debian does not use 'pockets' - return 'Release' + return "Release" # BPPH functions @@ -1351,11 +1429,15 @@ class SnapshotBPPH(object): def binaryFileUrls(self, include_meta=False): if include_meta: - return [{'url': self.getUrl(), - 'filename': self.getFileName(), - 'sha1': self._file.getHash(), - 'sha256': None, - 'size': self._file.size}] + return [ + { + "url": self.getUrl(), + "filename": self.getFileName(), + "sha1": self._file.getHash(), + "sha256": None, + "size": self._file.size, + } + ] return [self.getUrl()] def binaryFileUrl(self, filename): diff --git a/ubuntutools/builder.py b/ubuntutools/builder.py index c082602..943d3c7 100644 --- a/ubuntutools/builder.py +++ b/ubuntutools/builder.py @@ -22,6 +22,7 @@ import os import subprocess import logging + Logger = logging.getLogger(__name__) @@ -35,16 +36,17 @@ class Builder(object): def __init__(self, name): self.name = name cmd = ["dpkg-architecture", "-qDEB_BUILD_ARCH_CPU"] - self.architecture = subprocess.check_output(cmd, encoding='utf-8').strip() + self.architecture = subprocess.check_output(cmd, encoding="utf-8").strip() def _build_failure(self, returncode, dsc_file): if returncode != 0: - Logger.error("Failed to build %s from source with %s." % - (os.path.basename(dsc_file), self.name)) + Logger.error( + "Failed to build %s from source with %s." % (os.path.basename(dsc_file), self.name) + ) return returncode def exists_in_path(self): - for path in os.environ.get('PATH', os.defpath).split(os.pathsep): + for path in os.environ.get("PATH", os.defpath).split(os.pathsep): if os.path.isfile(os.path.join(path, self.name)): return True return False @@ -57,8 +59,7 @@ class Builder(object): def _update_failure(self, returncode, dist): if returncode != 0: - Logger.error("Failed to update %s chroot for %s." % - (dist, self.name)) + Logger.error("Failed to update %s chroot for %s." % (dist, self.name)) return returncode @@ -68,19 +69,39 @@ class Pbuilder(Builder): def build(self, dsc_file, dist, result_directory): _build_preparation(result_directory) - cmd = ["sudo", "-E", "ARCH=" + self.architecture, "DIST=" + dist, - self.name, "--build", - "--architecture", self.architecture, "--distribution", dist, - "--buildresult", result_directory, dsc_file] - Logger.debug(' '.join(cmd)) + cmd = [ + "sudo", + "-E", + "ARCH=" + self.architecture, + "DIST=" + dist, + self.name, + "--build", + "--architecture", + self.architecture, + "--distribution", + dist, + "--buildresult", + result_directory, + dsc_file, + ] + Logger.debug(" ".join(cmd)) returncode = subprocess.call(cmd) return self._build_failure(returncode, dsc_file) def update(self, dist): - cmd = ["sudo", "-E", "ARCH=" + self.architecture, "DIST=" + dist, - self.name, "--update", - "--architecture", self.architecture, "--distribution", dist] - Logger.debug(' '.join(cmd)) + cmd = [ + "sudo", + "-E", + "ARCH=" + self.architecture, + "DIST=" + dist, + self.name, + "--update", + "--architecture", + self.architecture, + "--distribution", + dist, + ] + Logger.debug(" ".join(cmd)) returncode = subprocess.call(cmd) return self._update_failure(returncode, dist) @@ -91,15 +112,22 @@ class Pbuilderdist(Builder): def build(self, dsc_file, dist, result_directory): _build_preparation(result_directory) - cmd = [self.name, dist, self.architecture, - "build", dsc_file, "--buildresult", result_directory] - Logger.debug(' '.join(cmd)) + cmd = [ + self.name, + dist, + self.architecture, + "build", + dsc_file, + "--buildresult", + result_directory, + ] + Logger.debug(" ".join(cmd)) returncode = subprocess.call(cmd) return self._build_failure(returncode, dsc_file) def update(self, dist): cmd = [self.name, dist, self.architecture, "update"] - Logger.debug(' '.join(cmd)) + Logger.debug(" ".join(cmd)) returncode = subprocess.call(cmd) return self._update_failure(returncode, dist) @@ -113,9 +141,8 @@ class Sbuild(Builder): workdir = os.getcwd() Logger.debug("cd " + result_directory) os.chdir(result_directory) - cmd = ["sbuild", "--arch-all", "--dist=" + dist, - "--arch=" + self.architecture, dsc_file] - Logger.debug(' '.join(cmd)) + cmd = ["sbuild", "--arch-all", "--dist=" + dist, "--arch=" + self.architecture, dsc_file] + Logger.debug(" ".join(cmd)) returncode = subprocess.call(cmd) Logger.debug("cd " + workdir) os.chdir(workdir) @@ -123,29 +150,29 @@ class Sbuild(Builder): def update(self, dist): cmd = ["schroot", "--list"] - Logger.debug(' '.join(cmd)) - process = subprocess.run(cmd, stdout=subprocess.PIPE, encoding='utf-8') + Logger.debug(" ".join(cmd)) + process = subprocess.run(cmd, stdout=subprocess.PIPE, encoding="utf-8") chroots, _ = process.stdout.strip().split() if process.returncode != 0: return process.returncode params = {"dist": dist, "arch": self.architecture} - for chroot in ("%(dist)s-%(arch)s-sbuild-source", - "%(dist)s-sbuild-source", - "%(dist)s-%(arch)s-source", - "%(dist)s-source"): + for chroot in ( + "%(dist)s-%(arch)s-sbuild-source", + "%(dist)s-sbuild-source", + "%(dist)s-%(arch)s-source", + "%(dist)s-source", + ): chroot = chroot % params if chroot in chroots: break else: return 1 - commands = [["sbuild-update"], - ["sbuild-distupgrade"], - ["sbuild-clean", "-a", "-c"]] + commands = [["sbuild-update"], ["sbuild-distupgrade"], ["sbuild-clean", "-a", "-c"]] for cmd in commands: # pylint: disable=W0631 - Logger.debug(' '.join(cmd) + " " + chroot) + Logger.debug(" ".join(cmd) + " " + chroot) ret = subprocess.call(cmd + [chroot]) # pylint: enable=W0631 if ret != 0: @@ -170,5 +197,4 @@ def get_builder(name): Logger.error("Builder doesn't appear to be installed: %s", name) else: Logger.error("Unsupported builder specified: %s.", name) - Logger.error("Supported builders: %s", - ", ".join(sorted(_SUPPORTED_BUILDERS.keys()))) + Logger.error("Supported builders: %s", ", ".join(sorted(_SUPPORTED_BUILDERS.keys()))) diff --git a/ubuntutools/config.py b/ubuntutools/config.py index dabfaa2..d1a9e2f 100644 --- a/ubuntutools/config.py +++ b/ubuntutools/config.py @@ -24,6 +24,7 @@ import sys import locale import logging + Logger = logging.getLogger(__name__) @@ -31,23 +32,24 @@ class UDTConfig(object): """Ubuntu Dev Tools configuration file (devscripts config file) and environment variable parsing. """ + no_conf = False # Package wide configuration variables. # These are reqired to be used by at least two scripts. defaults = { - 'BUILDER': 'pbuilder', - 'DEBIAN_MIRROR': 'http://deb.debian.org/debian', - 'DEBSEC_MIRROR': 'http://security.debian.org', - 'DEBIAN_DDEBS_MIRROR': 'http://debug.mirrors.debian.org/debian-debug', - 'LPINSTANCE': 'production', - 'MIRROR_FALLBACK': True, - 'UBUNTU_MIRROR': 'http://archive.ubuntu.com/ubuntu', - 'UBUNTU_PORTS_MIRROR': 'http://ports.ubuntu.com', - 'UBUNTU_INTERNAL_MIRROR': 'http://ftpmaster.internal/ubuntu', - 'UBUNTU_DDEBS_MIRROR': 'http://ddebs.ubuntu.com', - 'UPDATE_BUILDER': False, - 'WORKDIR': None, - 'KEYID': None, + "BUILDER": "pbuilder", + "DEBIAN_MIRROR": "http://deb.debian.org/debian", + "DEBSEC_MIRROR": "http://security.debian.org", + "DEBIAN_DDEBS_MIRROR": "http://debug.mirrors.debian.org/debian-debug", + "LPINSTANCE": "production", + "MIRROR_FALLBACK": True, + "UBUNTU_MIRROR": "http://archive.ubuntu.com/ubuntu", + "UBUNTU_PORTS_MIRROR": "http://ports.ubuntu.com", + "UBUNTU_INTERNAL_MIRROR": "http://ftpmaster.internal/ubuntu", + "UBUNTU_DDEBS_MIRROR": "http://ddebs.ubuntu.com", + "UPDATE_BUILDER": False, + "WORKDIR": None, + "KEYID": None, } # Populated from the configuration files: config = {} @@ -55,7 +57,7 @@ class UDTConfig(object): def __init__(self, no_conf=False, prefix=None): self.no_conf = no_conf if prefix is None: - prefix = os.path.basename(sys.argv[0]).upper().replace('-', '_') + prefix = os.path.basename(sys.argv[0]).upper().replace("-", "_") self.prefix = prefix if not no_conf: self.config = self.parse_devscripts_config() @@ -65,18 +67,21 @@ class UDTConfig(object): dictionary """ config = {} - for filename in ('/etc/devscripts.conf', '~/.devscripts'): + for filename in ("/etc/devscripts.conf", "~/.devscripts"): try: - f = open(os.path.expanduser(filename), 'r') + f = open(os.path.expanduser(filename), "r") except IOError: continue for line in f: parsed = shlex.split(line, comments=True) if len(parsed) > 1: - Logger.warning('Cannot parse variable assignment in %s: %s', - getattr(f, 'name', ''), line) - if len(parsed) >= 1 and '=' in parsed[0]: - key, value = parsed[0].split('=', 1) + Logger.warning( + "Cannot parse variable assignment in %s: %s", + getattr(f, "name", ""), + line, + ) + if len(parsed) >= 1 and "=" in parsed[0]: + key, value = parsed[0].split("=", 1) config[key] = value f.close() return config @@ -95,9 +100,9 @@ class UDTConfig(object): if default is None and key in self.defaults: default = self.defaults[key] - keys = [self.prefix + '_' + key] + keys = [self.prefix + "_" + key] if key in self.defaults: - keys.append('UBUNTUTOOLS_' + key) + keys.append("UBUNTUTOOLS_" + key) keys += compat_keys for k in keys: @@ -105,16 +110,19 @@ class UDTConfig(object): if k in store: value = store[k] if boolean: - if value in ('yes', 'no'): - value = value == 'yes' + if value in ("yes", "no"): + value = value == "yes" else: continue if k in compat_keys: - replacements = self.prefix + '_' + key + replacements = self.prefix + "_" + key if key in self.defaults: - replacements += 'or UBUNTUTOOLS_' + key - Logger.warning('Using deprecated configuration variable %s. ' - 'You should use %s.', k, replacements) + replacements += "or UBUNTUTOOLS_" + key + Logger.warning( + "Using deprecated configuration variable %s. You should use %s.", + k, + replacements, + ) return value return default @@ -132,7 +140,7 @@ def ubu_email(name=None, email=None, export=True): Return name, email. """ - name_email_re = re.compile(r'^\s*(.+?)\s*<(.+@.+)>\s*$') + name_email_re = re.compile(r"^\s*(.+?)\s*<(.+@.+)>\s*$") if email: match = name_email_re.match(email) @@ -140,11 +148,16 @@ def ubu_email(name=None, email=None, export=True): name = match.group(1) email = match.group(2) - if export and not name and not email and 'UBUMAIL' not in os.environ: + if export and not name and not email and "UBUMAIL" not in os.environ: export = False - for var, target in (('UBUMAIL', 'email'), ('DEBFULLNAME', 'name'), ('DEBEMAIL', 'email'), - ('EMAIL', 'email'), ('NAME', 'name')): + for var, target in ( + ("UBUMAIL", "email"), + ("DEBFULLNAME", "name"), + ("DEBEMAIL", "email"), + ("EMAIL", "email"), + ("NAME", "name"), + ): if name and email: break if var in os.environ: @@ -154,30 +167,30 @@ def ubu_email(name=None, email=None, export=True): name = match.group(1) if not email: email = match.group(2) - elif target == 'name' and not name: + elif target == "name" and not name: name = os.environ[var].strip() - elif target == 'email' and not email: + elif target == "email" and not email: email = os.environ[var].strip() if not name: - gecos_name = pwd.getpwuid(os.getuid()).pw_gecos.split(',')[0].strip() + gecos_name = pwd.getpwuid(os.getuid()).pw_gecos.split(",")[0].strip() if gecos_name: name = gecos_name if not email: mailname = socket.getfqdn() - if os.path.isfile('/etc/mailname'): - mailname = open('/etc/mailname', 'r').read().strip() - email = pwd.getpwuid(os.getuid()).pw_name + '@' + mailname + if os.path.isfile("/etc/mailname"): + mailname = open("/etc/mailname", "r").read().strip() + email = pwd.getpwuid(os.getuid()).pw_name + "@" + mailname if export: - os.environ['DEBFULLNAME'] = name - os.environ['DEBEMAIL'] = email + os.environ["DEBFULLNAME"] = name + os.environ["DEBEMAIL"] = email # decode env var or gecos raw string with the current locale's encoding encoding = locale.getdefaultlocale()[1] if not encoding: - encoding = 'utf-8' + encoding = "utf-8" if name and isinstance(name, bytes): name = name.decode(encoding) return name, email diff --git a/ubuntutools/lp/__init__.py b/ubuntutools/lp/__init__.py index 17e3542..d2c0400 100644 --- a/ubuntutools/lp/__init__.py +++ b/ubuntutools/lp/__init__.py @@ -2,5 +2,5 @@ # ubuntu-dev-tools Launchpad Python modules. # -service = 'production' -api_version = 'devel' +service = "production" +api_version = "devel" diff --git a/ubuntutools/lp/lpapicache.py b/ubuntutools/lp/lpapicache.py index 3d82cd7..f540084 100644 --- a/ubuntutools/lp/lpapicache.py +++ b/ubuntutools/lp/lpapicache.py @@ -35,47 +35,53 @@ from urllib.error import URLError from urllib.parse import urlparse from ubuntutools.version import Version -from ubuntutools.lp import (service, api_version) -from ubuntutools.misc import (download_text, - host_architecture, - DEFAULT_POCKETS, POCKETS, - DEFAULT_STATUSES, STATUSES) -from ubuntutools.lp.udtexceptions import (AlreadyLoggedInError, - ArchiveNotFoundException, - ArchSeriesNotFoundException, - PackageNotFoundException, - PocketDoesNotExistError, - SeriesNotFoundException) +from ubuntutools.lp import service, api_version +from ubuntutools.misc import ( + download_text, + host_architecture, + DEFAULT_POCKETS, + POCKETS, + DEFAULT_STATUSES, + STATUSES, +) +from ubuntutools.lp.udtexceptions import ( + AlreadyLoggedInError, + ArchiveNotFoundException, + ArchSeriesNotFoundException, + PackageNotFoundException, + PocketDoesNotExistError, + SeriesNotFoundException, +) import logging + Logger = logging.getLogger(__name__) __all__ = [ - 'Archive', - 'BinaryPackagePublishingHistory', - 'Build', - 'Distribution', - 'DistributionSourcePackage', - 'DistroSeries', - 'DistroArchSeries', - 'Launchpad', - 'PackageUpload', - 'PersonTeam', - 'Project', - 'ProjectSeries', - 'SourcePackagePublishingHistory', - ] + "Archive", + "BinaryPackagePublishingHistory", + "Build", + "Distribution", + "DistributionSourcePackage", + "DistroSeries", + "DistroArchSeries", + "Launchpad", + "PackageUpload", + "PersonTeam", + "Project", + "ProjectSeries", + "SourcePackagePublishingHistory", +] class _Launchpad(object): - '''Singleton for LP API access.''' + """Singleton for LP API access.""" def login(self, service=service, api_version=api_version): - '''Enforce a non-anonymous login.''' + """Enforce a non-anonymous login.""" if not self.logged_in: - self.__lp = LP.login_with('ubuntu-dev-tools', service, - version=api_version) + self.__lp = LP.login_with("ubuntu-dev-tools", service, version=api_version) # Unfortunately launchpadlib may 'login' using cached # credentials, without actually verifying if the credentials # are valid; which can lead to this 'login' not actually @@ -83,27 +89,26 @@ class _Launchpad(object): # So, this forces actual LP access here, to force actual login. self.__lp.me else: - raise AlreadyLoggedInError('Already logged in to Launchpad.') + raise AlreadyLoggedInError("Already logged in to Launchpad.") def login_anonymously(self, service=service, api_version=api_version): - '''Enforce an anonymous login.''' + """Enforce an anonymous login.""" if not self.logged_in: - self.__lp = LP.login_anonymously('ubuntu-dev-tools', service, - version=api_version) + self.__lp = LP.login_anonymously("ubuntu-dev-tools", service, version=api_version) else: - raise AlreadyLoggedInError('Already logged in to Launchpad.') + raise AlreadyLoggedInError("Already logged in to Launchpad.") def login_existing(self, lp): - '''Use an already logged in Launchpad object''' + """Use an already logged in Launchpad object""" if not self.logged_in: self.__lp = lp else: - raise AlreadyLoggedInError('Already logged in to Launchpad.') + raise AlreadyLoggedInError("Already logged in to Launchpad.") @property def logged_in(self): - '''Are we logged in?''' - return '_Launchpad__lp' in self.__dict__ + """Are we logged in?""" + return "_Launchpad__lp" in self.__dict__ def __getattr__(self, attr): if not self.logged_in: @@ -118,21 +123,22 @@ Launchpad = _Launchpad() class MetaWrapper(type): - ''' + """ A meta class used for wrapping LP API objects. - ''' + """ + def __init__(cls, name, bases, attrd): super(MetaWrapper, cls).__init__(name, bases, attrd) - if 'resource_type' not in attrd: - raise TypeError('Class "%s" needs an associated resource type' % - name) + if "resource_type" not in attrd: + raise TypeError('Class "%s" needs an associated resource type' % name) cls._cache = dict() class BaseWrapper(object, metaclass=MetaWrapper): - ''' + """ A base class from which other wrapper classes are derived. - ''' + """ + resource_type = None # it's a base class after all def __new__(cls, data): @@ -151,7 +157,7 @@ class BaseWrapper(object, metaclass=MetaWrapper): pass if isinstance(data, Entry): - (service_root, resource_type) = data.resource_type_link.split('#') + (service_root, resource_type) = data.resource_type_link.split("#") if service_root == str(Launchpad._root_uri) and resource_type in cls.resource_type: # check if it's already cached cached = cls._cache.get(data.self_link) @@ -163,21 +169,19 @@ class BaseWrapper(object, metaclass=MetaWrapper): cls._cache[data.self_link] = cached Logger.debug("%s: %s" % (cls.__name__, data.self_link)) # add additional class specific caching (if available) - cache = getattr(cls, 'cache', None) + cache = getattr(cls, "cache", None) if isinstance(cache, collections.abc.Callable): cache(cached) return cached else: - raise TypeError("'%s' is not a '%s' object" % - (str(data), str(cls.resource_type))) + raise TypeError("'%s' is not a '%s' object" % (str(data), str(cls.resource_type))) else: # not a LP API representation, let the specific class handle it - fetch = getattr(cls, 'fetch', None) + fetch = getattr(cls, "fetch", None) if isinstance(fetch, collections.abc.Callable): return fetch(data) else: - raise NotImplementedError("Don't know how to fetch '%s' from LP" - % str(data)) + raise NotImplementedError("Don't know how to fetch '%s' from LP" % str(data)) def __call__(self): return self._lpobject @@ -186,18 +190,18 @@ class BaseWrapper(object, metaclass=MetaWrapper): return getattr(self._lpobject, attr) def __repr__(self): - if hasattr(str, 'format'): - return '<{0}: {1!r}>'.format(self.__class__.__name__, - self._lpobject) + if hasattr(str, "format"): + return "<{0}: {1!r}>".format(self.__class__.__name__, self._lpobject) else: - return '<%s: %r>' % (self.__class__.__name__, self._lpobject) + return "<%s: %r>" % (self.__class__.__name__, self._lpobject) class Distribution(BaseWrapper): - ''' + """ Wrapper class around a LP distribution object. - ''' - resource_type = 'distribution' + """ + + resource_type = "distribution" def __init__(self, *args): self._archives = dict() @@ -210,18 +214,18 @@ class Distribution(BaseWrapper): self._cache[self.name] = self def _cache_series(self, series): - ''' + """ Add the DistroSeries to the cache if needed. - ''' + """ if series.version not in self._series: self._series_by_name[series.name] = series self._series[series.version] = series @classmethod def fetch(cls, dist): - ''' + """ Fetch the distribution object identified by 'dist' from LP. - ''' + """ if not isinstance(dist, str): raise TypeError("Don't know what do with '%r'" % dist) cached = cls._cache.get(dist) @@ -230,12 +234,12 @@ class Distribution(BaseWrapper): return cached def getArchive(self, archive=None): - ''' + """ Returns an Archive object for the requested archive. Raises a ArchiveNotFoundException if the archive doesn't exist. If 'archive' is None, return the main archive. - ''' + """ if archive: res = self._archives.get(archive) @@ -249,20 +253,19 @@ class Distribution(BaseWrapper): if res: return res else: - message = "The Archive '%s' doesn't exist in %s" % \ - (archive, self.display_name) + message = "The Archive '%s' doesn't exist in %s" % (archive, self.display_name) raise ArchiveNotFoundException(message) else: - if '_main_archive' not in self.__dict__: + if "_main_archive" not in self.__dict__: self._main_archive = Archive(self.main_archive_link) return self._main_archive def getSeries(self, name_or_version): - ''' + """ Returns a DistroSeries object for a series passed by name (e.g. 'karmic') or version (e.g. '9.10'). If the series is not found: raise SeriesNotFoundException - ''' + """ if name_or_version in self._series: return self._series[name_or_version] if name_or_version in self._series_by_name: @@ -271,17 +274,16 @@ class Distribution(BaseWrapper): try: series = DistroSeries(self().getSeries(name_or_version=name_or_version)) except HTTPError: - message = "Release '%s' is unknown in '%s'." % \ - (name_or_version, self.display_name) + message = "Release '%s' is unknown in '%s'." % (name_or_version, self.display_name) raise SeriesNotFoundException(message) self._cache_series(series) return series def getDevelopmentSeries(self): - ''' + """ Returns a DistroSeries object of the current development series. - ''' + """ if not self._dev_series: series = DistroSeries(self.current_series_link) self._cache_series(series) @@ -289,89 +291,88 @@ class Distribution(BaseWrapper): return self._dev_series def getAllSeries(self, active=True): - ''' + """ Returns a list of all DistroSeries objects. - ''' + """ if not self._have_all_series: for s in Launchpad.load(self.series_collection_link).entries: - series = DistroSeries(s['self_link']) + series = DistroSeries(s["self_link"]) self._cache_series(series) self._have_all_series = True allseries = filter(lambda s: s.active, self._series.values()) - allseries = sorted(allseries, - key=lambda s: float(s.version), - reverse=True) - Logger.debug("Found series: %s" % ", ".join(map(lambda s: "%s (%s)" % - (s.name, s.version), - allseries))) + allseries = sorted(allseries, key=lambda s: float(s.version), reverse=True) + Logger.debug( + "Found series: %s" + % ", ".join(map(lambda s: "%s (%s)" % (s.name, s.version), allseries)) + ) return collections.OrderedDict((s.name, s) for s in allseries) class DistroArchSeries(BaseWrapper): - ''' + """ Wrapper class around a LP distro arch series object. - ''' - resource_type = 'distro_arch_series' + """ + + resource_type = "distro_arch_series" def getSeries(self): - ''' + """ Get DistroSeries for this. - ''' + """ return DistroSeries(self._lpobject.distroseries_link) class DistroSeries(BaseWrapper): - ''' + """ Wrapper class around a LP distro series object. - ''' - resource_type = 'distro_series' + """ + + resource_type = "distro_series" def __init__(self, *args): if "_architectures" not in self.__dict__: self._architectures = dict() def getArchSeries(self, archtag=None): - ''' + """ Returns a DistroArchSeries object for an architecture passed by name (e.g. 'amd64'). If arch is not specified, get the DistroArchSeries for the system arch. The special archtag 'all' will get the system arch. If the architecture is not found: raise ArchSeriesNotFoundException. - ''' - if not archtag or archtag == 'all': + """ + if not archtag or archtag == "all": archtag = host_architecture() if archtag not in self._architectures: try: - architecture = DistroArchSeries( - self().getDistroArchSeries(archtag=archtag)) - self._architectures[architecture.architecture_tag] = ( - architecture) + architecture = DistroArchSeries(self().getDistroArchSeries(archtag=archtag)) + self._architectures[architecture.architecture_tag] = architecture except HTTPError: message = "Architecture %s is unknown." % archtag raise ArchSeriesNotFoundException(message) return self._architectures[archtag] - def getPackageUploads(self, name=None, pocket=None, version=None, - status='Unapproved'): - '''Returns a list of PackageUploads for this series.''' - params = {'exact_match': True} + def getPackageUploads(self, name=None, pocket=None, version=None, status="Unapproved"): + """Returns a list of PackageUploads for this series.""" + params = {"exact_match": True} if name: - params['name'] = name + params["name"] = name if pocket: - params['pocket'] = pocket + params["pocket"] = pocket if version: - params['version'] = version + params["version"] = version if status: - params['status'] = status + params["status"] = status return [PackageUpload(p) for p in self._lpobject.getPackageUploads(**params)] class PackageUpload(BaseWrapper): - ''' + """ Wrapper class around a LP package_upload object. - ''' - resource_type = 'package_upload' + """ + + resource_type = "package_upload" def __init__(self, *args): self._custom_urls = None @@ -426,10 +427,11 @@ class PackageUpload(BaseWrapper): class Archive(BaseWrapper): - ''' + """ Wrapper class around a LP archive object. - ''' - resource_type = 'archive' + """ + + resource_type = "archive" def __init__(self, *args): self._binpkgs = {} @@ -438,9 +440,17 @@ class Archive(BaseWrapper): self._pkgset_uploaders = {} self._component_uploaders = {} - def getSourcePackage(self, name, series=None, pocket=None, version=None, - status=None, wrapper=None, search_all_series=False): - ''' + def getSourcePackage( + self, + name, + series=None, + pocket=None, + version=None, + status=None, + wrapper=None, + search_all_series=False, + ): + """ Returns a SourcePackagePublishingHistory object for the most recent source package in the distribution 'dist', series and pocket. @@ -470,20 +480,33 @@ class Archive(BaseWrapper): If the requested source package doesn't exist a PackageNotFoundException is raised. - ''' - return self._getPublishedItem(name, series, pocket, cache=self._srcpkgs, - function='getPublishedSources', - name_key='source_name', - wrapper=wrapper or SourcePackagePublishingHistory, - version=version, - status=status, - search_all_series=search_all_series, - binary=False) + """ + return self._getPublishedItem( + name, + series, + pocket, + cache=self._srcpkgs, + function="getPublishedSources", + name_key="source_name", + wrapper=wrapper or SourcePackagePublishingHistory, + version=version, + status=status, + search_all_series=search_all_series, + binary=False, + ) - def getBinaryPackage(self, name, archtag=None, series=None, pocket=None, - version=None, status=None, wrapper=None, - search_all_series=False): - ''' + def getBinaryPackage( + self, + name, + archtag=None, + series=None, + pocket=None, + version=None, + status=None, + wrapper=None, + search_all_series=False, + ): + """ Returns a BinaryPackagePublishingHistory object for the most recent source package in the distribution 'dist', architecture 'archtag', series and pocket. @@ -514,26 +537,42 @@ class Archive(BaseWrapper): If the requested binary package doesn't exist a PackageNotFoundException is raised. - ''' - return self._getPublishedItem(name, series, pocket, archtag=archtag, - cache=self._binpkgs, - function='getPublishedBinaries', - name_key='binary_name', - wrapper=wrapper or BinaryPackagePublishingHistory, - version=version, - status=status, - search_all_series=search_all_series, - binary=True) + """ + return self._getPublishedItem( + name, + series, + pocket, + archtag=archtag, + cache=self._binpkgs, + function="getPublishedBinaries", + name_key="binary_name", + wrapper=wrapper or BinaryPackagePublishingHistory, + version=version, + status=status, + search_all_series=search_all_series, + binary=True, + ) - def _getPublishedItem(self, name, series, pocket, cache, - function, name_key, wrapper, archtag=None, - version=None, status=None, search_all_series=False, - binary=False): - ''' + def _getPublishedItem( + self, + name, + series, + pocket, + cache, + function, + name_key, + wrapper, + archtag=None, + version=None, + status=None, + search_all_series=False, + binary=False, + ): + """ Common code between getSourcePackage and getBinaryPackage. Don't use this directly. - ''' + """ if not pocket: if version and not series: # check ALL pockets if specific version in any series @@ -604,56 +643,53 @@ class Archive(BaseWrapper): if archtag is None: archtag = host_architecture() - index = (name, getattr(series, 'name', None), archtag, pockets, statuses, version) + index = (name, getattr(series, "name", None), archtag, pockets, statuses, version) if index in cache: return cache[index] - params = { - name_key: name, - 'exact_match': True, - } + params = {name_key: name, "exact_match": True} if arch_series: - params['distro_arch_series'] = arch_series() + params["distro_arch_series"] = arch_series() elif series: - params['distro_series'] = series() + params["distro_series"] = series() if len(pockets) == 1: - params['pocket'] = pockets[0] + params["pocket"] = pockets[0] if len(statuses) == 1: - params['status'] = statuses[0] + params["status"] = statuses[0] if version: - params['version'] = version + params["version"] = version - Logger.debug('Calling %s(%s)' % (function, - ', '.join(['%s=%s' % (k, v) - for (k, v) in params.items()]))) + Logger.debug( + "Calling %s(%s)" + % (function, ", ".join(["%s=%s" % (k, v) for (k, v) in params.items()])) + ) records = getattr(self, function)(**params) - err_msg = ("does not exist in the %s %s archive" % - (dist.display_name, self.name)) + err_msg = "does not exist in the %s %s archive" % (dist.display_name, self.name) for record in records: if binary: - rversion = getattr(record, 'binary_package_version', None) + rversion = getattr(record, "binary_package_version", None) else: - rversion = getattr(record, 'source_package_version', None) - skipmsg = ('Skipping version %s: ' % rversion) + rversion = getattr(record, "source_package_version", None) + skipmsg = "Skipping version %s: " % rversion if record.pocket not in pockets: - err_msg = 'pocket %s not in (%s)' % (record.pocket, ','.join(pockets)) + err_msg = "pocket %s not in (%s)" % (record.pocket, ",".join(pockets)) Logger.debug(skipmsg + err_msg) continue if record.status not in statuses: - err_msg = 'status %s not in (%s)' % (record.status, ','.join(statuses)) + err_msg = "status %s not in (%s)" % (record.status, ",".join(statuses)) Logger.debug(skipmsg + err_msg) continue r = wrapper(record) if binary and archtag and archtag != r.arch: - err_msg = 'arch %s does not match requested arch %s' % (r.arch, archtag) + err_msg = "arch %s does not match requested arch %s" % (r.arch, archtag) Logger.debug(skipmsg + err_msg) continue # results are ordered so first is latest @@ -667,15 +703,15 @@ class Archive(BaseWrapper): for epoch in range(1, 9): v = Version(version) v.epoch = epoch - params['version'] = v.full_version + params["version"] = v.full_version if len(getattr(self, function)(**params)) > 0: version_with_epoch = v.full_version - Logger.debug('Found version with epoch %s' % version_with_epoch) + Logger.debug("Found version with epoch %s" % version_with_epoch) break - if name_key == 'binary_name': + if name_key == "binary_name": package_type = "binary package" - elif name_key == 'source_name': + elif name_key == "source_name": package_type = "source package" else: package_type = "package" @@ -690,30 +726,38 @@ class Archive(BaseWrapper): if len(pockets) == 1: msg += " for pocket %s" % pockets[0] elif len(pockets) != len(POCKETS): - msg += " for pockets " + ', '.join(pockets) + msg += " for pockets " + ", ".join(pockets) elif series: msg += " in %s" % series.name if len(pockets) == 1: msg += "-%s" % pockets[0] elif len(pockets) != len(POCKETS): - msg += " for pockets " + ', '.join(pockets) + msg += " for pockets " + ", ".join(pockets) if len(statuses) == 1: msg += " with status %s" % statuses[0] elif len(statuses) != len(STATUSES): - msg += " with status in " + ', '.join(statuses) + msg += " with status in " + ", ".join(statuses) if version_with_epoch: msg += " (did you forget the epoch? try %s)" % version_with_epoch raise PackageNotFoundException(msg) - def copyPackage(self, source_name, version, from_archive, to_pocket, - to_series=None, sponsored=None, include_binaries=False): - '''Copy a single named source into this archive. + def copyPackage( + self, + source_name, + version, + from_archive, + to_pocket, + to_series=None, + sponsored=None, + include_binaries=False, + ): + """Copy a single named source into this archive. Asynchronously copy a specific version of a named source to the destination archive if necessary. Calls to this method will return immediately if the copy passes basic security checks and the copy will happen sometime later with full checking. - ''' + """ if isinstance(sponsored, PersonTeam): sponsored = sponsored._lpobject @@ -725,67 +769,75 @@ class Archive(BaseWrapper): to_pocket=to_pocket, to_series=to_series, sponsored=sponsored, - include_binaries=include_binaries - ) + include_binaries=include_binaries, + ) def getUploadersForComponent(self, component_name): - '''Get the list of PersonTeams who can upload packages in the + """Get the list of PersonTeams who can upload packages in the specified component. [Note: the permission records, themselves, aren't exposed] - ''' + """ if component_name not in self._component_uploaders: - self._component_uploaders[component_name] = sorted(set( - PersonTeam(permission.person_link) for permission in - self._lpobject.getUploadersForComponent(component_name=component_name) - )) + self._component_uploaders[component_name] = sorted( + set( + PersonTeam(permission.person_link) + for permission in self._lpobject.getUploadersForComponent( + component_name=component_name + ) + ) + ) return self._component_uploaders[component_name] def getUploadersForPackage(self, source_package_name): - '''Get the list of PersonTeams who can upload source_package_name) + """Get the list of PersonTeams who can upload source_package_name) [Note: the permission records, themselves, aren't exposed] - ''' + """ if source_package_name not in self._pkg_uploaders: self._pkg_uploaders[source_package_name] = sorted( set( - PersonTeam(permission.person_link) for permission in - self._lpobject.getUploadersForPackage(source_package_name=source_package_name) + PersonTeam(permission.person_link) + for permission in self._lpobject.getUploadersForPackage( + source_package_name=source_package_name + ) ), - key=lambda s: s.name + key=lambda s: s.name, ) return self._pkg_uploaders[source_package_name] def getUploadersForPackageset(self, packageset, direct_permissions=False): - '''Get the list of PersonTeams who can upload packages in packageset + """Get the list of PersonTeams who can upload packages in packageset [Note: the permission records, themselves, aren't exposed] - ''' + """ key = (packageset, direct_permissions) if key not in self._pkgset_uploaders: - self._pkgset_uploaders[key] = sorted(set( - PersonTeam(permission.person_link) for permission in - self._lpobject.getUploadersForPackageset( - packageset=packageset._lpobject, - direct_permissions=direct_permissions, + self._pkgset_uploaders[key] = sorted( + set( + PersonTeam(permission.person_link) + for permission in self._lpobject.getUploadersForPackageset( + packageset=packageset._lpobject, direct_permissions=direct_permissions + ) ) - )) + ) return self._pkgset_uploaders[key] def getMySubscriptionURL(self): - '''Get the "subscription URL" for the logged in user + """Get the "subscription URL" for the logged in user If this is a private archive (i.e. private PPA), this returns the "subscription URL" including authentication; otherwise this returns None. - ''' + """ if self.private: return PersonTeam.me.getArchiveSubscriptionURL(archive=self._lpobject) return None class SourcePackagePublishingHistory(BaseWrapper): - ''' + """ Wrapper class around a LP source package object. - ''' - resource_type = 'source_package_publishing_history' + """ + + resource_type = "source_package_publishing_history" def __init__(self, *args): self._archive = None @@ -795,73 +847,74 @@ class SourcePackagePublishingHistory(BaseWrapper): self._source_urls = None # Don't share _builds between different # SourcePackagePublishingHistory objects - if '_builds' not in self.__dict__: + if "_builds" not in self.__dict__: self._builds = dict() def getDistroSeries(self): - ''' + """ Return the DistroSeries. - ''' + """ if not self._distro_series: self._distro_series = DistroSeries(self._lpobject.distro_series_link) return self._distro_series def getPackageName(self): - ''' + """ Returns the source package name. - ''' + """ return self._lpobject.source_package_name def getVersion(self): - ''' + """ Returns the version of the source package. - ''' + """ return self._lpobject.source_package_version def getComponent(self): - ''' + """ Returns the component of the source package. - ''' + """ return self._lpobject.component_name def getSeriesName(self): - ''' + """ Returns the series Named getSeriesName() to avoid confusion with getDistroSeries() - ''' + """ return self.getDistroSeries().name def getSeriesAndPocket(self): - ''' + """ Returns a human-readable release-pocket - ''' + """ release = self.getSeriesName() - if self.pocket != 'Release': - release += '-' + self.pocket.lower() + if self.pocket != "Release": + release += "-" + self.pocket.lower() return release def getArchive(self): - ''' + """ Get this SPPH's archive. - ''' + """ if not self._archive: self._archive = Archive(self._lpobject.archive_link) return self._archive def getChangelog(self, since_version=None): - ''' + """ Return the changelog, optionally since a particular version May return None if the changelog isn't available Only available in the devel API, not 1.0 - ''' + """ if self._changelog is None: url = self._lpobject.changelogUrl() if url is None: - Logger.error('No changelog available for %s %s' % - (self.getPackageName(), self.getVersion())) + Logger.error( + "No changelog available for %s %s" % (self.getPackageName(), self.getVersion()) + ) return None try: @@ -881,10 +934,10 @@ class SourcePackagePublishingHistory(BaseWrapper): if block.version <= since_version: break new_entries.append(str(block)) - return ''.join(new_entries) + return "".join(new_entries) def sourceFileUrls(self, include_meta=False): - ''' + """ Return the URL for this source publication's files. The include_meta param changes the return value; @@ -899,76 +952,77 @@ class SourcePackagePublishingHistory(BaseWrapper): filename: the filename parsed from the url path Note that while all the keys will be in the dict, their values may be None. - ''' + """ if not self._source_urls: urls = self._lpobject.sourceFileUrls(include_meta=True) if not urls: - Logger.warning('SPPH %s_%s has no sourceFileUrls' % - (self.getPackageName(), self.getVersion())) + Logger.warning( + "SPPH %s_%s has no sourceFileUrls" % (self.getPackageName(), self.getVersion()) + ) for u in urls: # make sure mandatory fields are present - for field in ['url', 'sha1', 'sha256', 'size']: + for field in ["url", "sha1", "sha256", "size"]: if field not in u: u[field] = None - u['filename'] = os.path.basename(urlparse(u['url']).path) + u["filename"] = os.path.basename(urlparse(u["url"]).path) self._source_urls = urls if include_meta: return list(self._source_urls) - return [f['url'] for f in self._source_urls] + return [f["url"] for f in self._source_urls] def sourceFileUrl(self, filename): - ''' + """ Returns the URL for the specified source filename. If the filename is not found in the sourceFileUrls(), this returns None. - ''' + """ for f in self.sourceFileUrls(include_meta=True): - if filename == f['filename']: - return f['url'] + if filename == f["filename"]: + return f["url"] return None def sourceFileSha1(self, url_or_filename): - ''' + """ Returns the SHA1 checksum for the specified source file url. If the url is not found in the sourceFileUrls(), this returns None. The url may be specified as a filename. - ''' + """ for f in self.sourceFileUrls(include_meta=True): - if url_or_filename in [f['url'], f['filename']]: - return f['sha1'] + if url_or_filename in [f["url"], f["filename"]]: + return f["sha1"] return None def sourceFileSha256(self, url_or_filename): - ''' + """ Returns the SHA256 checksum for the specified source file url. If the url is not found in the sourceFileUrls(), this returns None. The url may be specified as a filename. - ''' + """ for f in self.sourceFileUrls(include_meta=True): - if url_or_filename in [f['url'], f['filename']]: - return f['sha256'] + if url_or_filename in [f["url"], f["filename"]]: + return f["sha256"] return None def sourceFileSize(self, url_or_filename): - ''' + """ Returns the size for the specified source file url. If the url is not found in the sourceFileUrls(), this returns 0. The url may be specified as a filename. - ''' + """ for f in self.sourceFileUrls(include_meta=True): - if url_or_filename in [f['url'], f['filename']]: - return int(f['size']) + if url_or_filename in [f["url"], f["filename"]]: + return int(f["size"]) return 0 def getBinaries(self, arch=None, name=None, ext=None): - ''' + """ Returns the resulting BinaryPackagePublishingHistorys. If arch is specified, it returns binaries for only that arch, plus any binaries with arch 'all'. If arch is not specified, or @@ -977,14 +1031,13 @@ class SourcePackagePublishingHistory(BaseWrapper): If name is specified, only returns BPPH matching that (regex) name. If ext is specified, only returns BPPH matching that (regex) ext. - ''' - if arch == 'all': + """ + if arch == "all": arch = None if self.status in ["Pending", "Published"]: # Published, great! Directly query the list of binaries - binaries = map(BinaryPackagePublishingHistory, - self._lpobject.getPublishedBinaries()) + binaries = map(BinaryPackagePublishingHistory, self._lpobject.getPublishedBinaries()) for b in binaries: a = b.arch if a not in self._binaries: @@ -999,12 +1052,12 @@ class SourcePackagePublishingHistory(BaseWrapper): # strip out the URL leading text. filename = os.path.basename(urlparse(url).path) # strip the file suffix - (pkgname, _, e) = filename.rpartition('.') + (pkgname, _, e) = filename.rpartition(".") # split into name, version, arch - (n, v, a) = pkgname.rsplit('_', 2) + (n, v, a) = pkgname.rsplit("_", 2) # arch 'all' has separate bpph for each real arch, # but all point to the same binary url - if a == 'all': + if a == "all": a = arch or host_architecture() # Only check the arch requested - saves time if arch and arch != a: @@ -1021,9 +1074,7 @@ class SourcePackagePublishingHistory(BaseWrapper): # we ignore the version, as it may be missing epoch # also we can't use series, as some package versions # span multiple series! (e.g. for different archs) - params = {'name': n, - 'archtag': a, - 'version': self.getVersion()} + params = {"name": n, "archtag": a, "version": self.getVersion()} try: bpph = archive.getBinaryPackage(**params) except PackageNotFoundException: @@ -1049,7 +1100,7 @@ class SourcePackagePublishingHistory(BaseWrapper): return bpphs def _fetch_builds(self): - '''Populate self._builds with the build records.''' + """Populate self._builds with the build records.""" builds = self.getBuilds() for build in builds: self._builds[build.arch_tag] = Build(build) @@ -1063,9 +1114,8 @@ class SourcePackagePublishingHistory(BaseWrapper): for arch in archs: build = self._builds.get(arch) if build: - res.append(' %s' % build) - return "Build state(s) for '%s':\n%s" % ( - self.getPackageName(), '\n'.join(res)) + res.append(" %s" % build) + return "Build state(s) for '%s':\n%s" % (self.getPackageName(), "\n".join(res)) def rescoreBuilds(self, archs, score): res = list() @@ -1077,11 +1127,14 @@ class SourcePackagePublishingHistory(BaseWrapper): build = self._builds.get(arch) if build: if build.rescore(score): - res.append(' %s: done' % arch) + res.append(" %s: done" % arch) else: - res.append(' %s: failed' % arch) + res.append(" %s: failed" % arch) return "Rescoring builds of '%s' to %i:\n%s" % ( - self.getPackageName(), score, '\n'.join(res)) + self.getPackageName(), + score, + "\n".join(res), + ) def retryBuilds(self, archs): res = list() @@ -1093,18 +1146,18 @@ class SourcePackagePublishingHistory(BaseWrapper): build = self._builds.get(arch) if build: if build.retry(): - res.append(' %s: done' % arch) + res.append(" %s: done" % arch) else: - res.append(' %s: failed' % arch) - return "Retrying builds of '%s':\n%s" % ( - self.getPackageName(), '\n'.join(res)) + res.append(" %s: failed" % arch) + return "Retrying builds of '%s':\n%s" % (self.getPackageName(), "\n".join(res)) class BinaryPackagePublishingHistory(BaseWrapper): - ''' + """ Wrapper class around a LP binary package object. - ''' - resource_type = 'binary_package_publishing_history' + """ + + resource_type = "binary_package_publishing_history" def __init__(self, *args): self._arch = None @@ -1119,31 +1172,31 @@ class BinaryPackagePublishingHistory(BaseWrapper): return self._arch def getSourcePackageName(self): - ''' + """ Returns the source package name. - ''' + """ return self.getBuild().source_package_name def getPackageName(self): - ''' + """ Returns the binary package name. - ''' + """ return self._lpobject.binary_package_name def getVersion(self): - ''' + """ Returns the version of the binary package. - ''' + """ return self._lpobject.binary_package_version def getComponent(self): - ''' + """ Returns the component of the binary package. - ''' + """ return self._lpobject.component_name def binaryFileUrls(self, include_meta=False): - ''' + """ Return the URL for this binary publication's files. Only available in the devel API, not 1.0 @@ -1160,110 +1213,114 @@ class BinaryPackagePublishingHistory(BaseWrapper): filename: the filename parsed from the url path Note that while all the keys will be in the dict, their values may be None. - ''' + """ if not self._binary_urls: try: urls = self._lpobject.binaryFileUrls(include_meta=True) except AttributeError: - raise AttributeError("binaryFileUrls can only be found in lpapi " - "devel, not 1.0. Login using devel to have it.") + raise AttributeError( + "binaryFileUrls can only be found in lpapi " + "devel, not 1.0. Login using devel to have it." + ) if not urls: - Logger.warning('BPPH %s_%s has no binaryFileUrls' % - (self.getPackageName(), self.getVersion())) + Logger.warning( + "BPPH %s_%s has no binaryFileUrls" % (self.getPackageName(), self.getVersion()) + ) for u in urls: # make sure mandatory fields are present - for field in ['url', 'sha1', 'sha256', 'size']: + for field in ["url", "sha1", "sha256", "size"]: if field not in u: u[field] = None - u['filename'] = os.path.basename(urlparse(u['url']).path) + u["filename"] = os.path.basename(urlparse(u["url"]).path) self._binary_urls = urls if include_meta: return list(self._binary_urls) - return [f['url'] for f in self._binary_urls] + return [f["url"] for f in self._binary_urls] def binaryFileUrl(self, filename): - ''' + """ Returns the URL for the specified binary filename. If the filename is not found in the binaryFileUrls(), this returns None. - ''' + """ for f in self.binaryFileUrls(include_meta=True): - if filename == f['filename']: - return f['url'] + if filename == f["filename"]: + return f["url"] return None def binaryFileSha1(self, url_or_filename): - ''' + """ Returns the SHA1 checksum for the specified binary file url. If the url is not found in the binaryFileUrls(), this returns None. The url may be specified as a filename. - ''' + """ for f in self.binaryFileUrls(include_meta=True): - if url_or_filename in [f['url'], f['filename']]: - return f['sha1'] + if url_or_filename in [f["url"], f["filename"]]: + return f["sha1"] return None def binaryFileSha256(self, url_or_filename): - ''' + """ Returns the SHA256 checksum for the specified binary file url. If the url is not found in the binaryFileUrls(), this returns None. The url may be specified as a filename. - ''' + """ for f in self.binaryFileUrls(include_meta=True): - if url_or_filename in [f['url'], f['filename']]: - return f['sha256'] + if url_or_filename in [f["url"], f["filename"]]: + return f["sha256"] return None def binaryFileSize(self, url_or_filename): - ''' + """ Returns the size for the specified binary file url. If the url is not found in the binaryFileUrls(), this returns 0. The url may be specified as a filename. - ''' + """ for f in self.binaryFileUrls(include_meta=True): - if url_or_filename in [f['url'], f['filename']]: - return int(f['size']) + if url_or_filename in [f["url"], f["filename"]]: + return int(f["size"]) return 0 def getBuild(self): - ''' + """ Returns the original build of the binary package. - ''' + """ return Build(self._lpobject.build_link) def getUrl(self): - ''' + """ Returns the original build URL of the binary package. - ''' - return "{build}/+files/{filename}".format(build=self.getBuild().getUrl(), - filename=self.getFileName()) + """ + return "{build}/+files/{filename}".format( + build=self.getBuild().getUrl(), filename=self.getFileName() + ) def getFileVersion(self): - ''' + """ Returns the file version, which is the package version without the epoch - ''' + """ return Version(self.getVersion()).strip_epoch() def getFileArch(self): - ''' + """ Returns the file arch, which is 'all' if not arch-specific - ''' + """ if bool(self._lpobject.architecture_specific): return self.arch else: - return 'all' + return "all" def getFileExt(self): - ''' + """ Returns the file extension; "deb", "ddeb", or "udeb". - ''' + """ if not self._ext: self._ext = self._getFileExt() @@ -1272,9 +1329,9 @@ class BinaryPackagePublishingHistory(BaseWrapper): def _getFileExt(self): try: # this is the best way, from the actual URL filename - return self.binaryFileUrls()[0].rpartition('.')[2] + return self.binaryFileUrls()[0].rpartition(".")[2] except (AttributeError, IndexError): - Logger.debug('Could not get file ext from url, trying to guess...') + Logger.debug("Could not get file ext from url, trying to guess...") # is_debug should be reliable way of detecting ddeb...? try: @@ -1293,22 +1350,24 @@ class BinaryPackagePublishingHistory(BaseWrapper): return "deb" def getFileName(self): - ''' + """ Returns the filename for this binary package. - ''' - return "{name}_{version}_{arch}.{ext}".format(name=self.getPackageName(), - version=self.getFileVersion(), - arch=self.getFileArch(), - ext=self.getFileExt()) + """ + return "{name}_{version}_{arch}.{ext}".format( + name=self.getPackageName(), + version=self.getFileVersion(), + arch=self.getFileArch(), + ext=self.getFileExt(), + ) class MetaPersonTeam(MetaWrapper): @property def me(cls): - '''The PersonTeam object of the currently authenticated LP user or + """The PersonTeam object of the currently authenticated LP user or None when anonymously logged in. - ''' - if '_me' not in cls.__dict__: + """ + if "_me" not in cls.__dict__: try: # We have to use me.self_link due to LP: #504297 cls._me = PersonTeam(Launchpad.me.self_link) @@ -1322,29 +1381,29 @@ class MetaPersonTeam(MetaWrapper): class PersonTeam(BaseWrapper, metaclass=MetaPersonTeam): - ''' + """ Wrapper class around a LP person or team object. - ''' + """ - resource_type = ('person', 'team') + resource_type = ("person", "team") def __init__(self, *args): # Don't share _upload between different PersonTeams self._ppas = None - if '_upload' not in self.__dict__: + if "_upload" not in self.__dict__: self._upload = dict() def __str__(self): - return u'%s (%s)' % (self.display_name, self.name) + return "%s (%s)" % (self.display_name, self.name) def cache(self): self._cache[self.name] = self @classmethod def fetch(cls, person_or_team): - ''' + """ Fetch the person or team object identified by 'url' from LP. - ''' + """ if not isinstance(person_or_team, str): raise TypeError("Don't know what do with '%r'" % person_or_team) cached = cls._cache.get(person_or_team) @@ -1353,39 +1412,35 @@ class PersonTeam(BaseWrapper, metaclass=MetaPersonTeam): return cached def isLpTeamMember(self, team): - ''' + """ Checks if the user is a member of a certain team on Launchpad. Returns True if the user is a member of the team otherwise False. - ''' + """ return any(t.name == team for t in self.super_teams) - def canUploadPackage(self, archive, distroseries, package, component, - pocket='Release'): - '''Check if the person or team has upload rights for the source + def canUploadPackage(self, archive, distroseries, package, component, pocket="Release"): + """Check if the person or team has upload rights for the source package to the specified 'archive' and 'distrorelease'. A source package name and a component have to be specified. 'archive' has to be a Archive object. 'distroseries' has to be an DistroSeries object. - ''' + """ if not isinstance(archive, Archive): raise TypeError("'%r' is not an Archive object." % archive) if not isinstance(distroseries, DistroSeries): raise TypeError("'%r' is not a DistroSeries object." % distroseries) if package is not None and not isinstance(package, str): - raise TypeError('A source package name expected.') + raise TypeError("A source package name expected.") if component is not None and not isinstance(component, str): - raise TypeError('A component name expected.') + raise TypeError("A component name expected.") if package is None and component is None: - raise ValueError('Either a source package name or a component has ' - 'to be specified.') + raise ValueError("Either a source package name or a component has to be specified.") if pocket not in POCKETS: - raise PocketDoesNotExistError("Pocket '%s' does not exist." % - pocket) + raise PocketDoesNotExistError("Pocket '%s' does not exist." % pocket) - canUpload = self._upload.get((archive, distroseries, pocket, package, - component)) + canUpload = self._upload.get((archive, distroseries, pocket, package, component)) if canUpload is None: # checkUpload() throws an exception if the person can't upload @@ -1410,8 +1465,10 @@ class PersonTeam(BaseWrapper, metaclass=MetaPersonTeam): def getPPAs(self): if self._ppas is None: - ppas = [Archive(ppa['self_link']) for ppa in - Launchpad.load(self._lpobject.ppas_collection_link).entries] + ppas = [ + Archive(ppa["self_link"]) + for ppa in Launchpad.load(self._lpobject.ppas_collection_link).entries + ] self._ppas = {ppa.name: ppa for ppa in ppas} return self._ppas @@ -1420,10 +1477,11 @@ class PersonTeam(BaseWrapper, metaclass=MetaPersonTeam): class Project(BaseWrapper): - ''' + """ Wrapper class around a LP project object. - ''' - resource_type = ('project') + """ + + resource_type = "project" def __init__(self, *args): self._series = None @@ -1435,41 +1493,45 @@ class Project(BaseWrapper): The list will be sorted by date_created, in descending order. """ if not self._series: - series = [ProjectSeries(s['self_link']) for s in - Launchpad.load(self._lpobject.series_collection_link).entries] + series = [ + ProjectSeries(s["self_link"]) + for s in Launchpad.load(self._lpobject.series_collection_link).entries + ] self._series = sorted(series, key=lambda s: s.date_created, reverse=True) return self._series.copy() @classmethod def fetch(cls, project): - ''' + """ Fetch the project object identified by 'project' from LP. - ''' + """ if not isinstance(project, str): raise TypeError("Don't know what do with '%r'" % project) return Project(Launchpad.projects(project)) class ProjectSeries(BaseWrapper): - ''' + """ Wrapper class around a LP project_series object. - ''' - resource_type = ('project_series') + """ + + resource_type = "project_series" class Build(BaseWrapper): - ''' + """ Wrapper class around a build object. - ''' - resource_type = 'build' + """ + + resource_type = "build" def __str__(self): - return u'%s: %s' % (self.arch_tag, self.buildstate) + return "%s: %s" % (self.arch_tag, self.buildstate) def getSourcePackagePublishingHistory(self): link = self._lpobject.current_source_publication_link if link: - if re.search('redacted', link): + if re.search("redacted", link): # Too old - the link has been 'redacted' return None return SourcePackagePublishingHistory(link) @@ -1492,38 +1554,38 @@ class Build(BaseWrapper): class DistributionSourcePackage(BaseWrapper): - ''' + """ Caching class for distribution_source_package objects. - ''' - resource_type = 'distribution_source_package' + """ + + resource_type = "distribution_source_package" class Packageset(BaseWrapper): - ''' + """ Caching class for packageset objects. - ''' - resource_type = 'packageset' + """ + + resource_type = "packageset" _lp_packagesets = None _source_sets = {} @classmethod - def setsIncludingSource(cls, sourcepackagename, distroseries=None, - direct_inclusion=False): - '''Get the package sets including sourcepackagename''' + def setsIncludingSource(cls, sourcepackagename, distroseries=None, direct_inclusion=False): + """Get the package sets including sourcepackagename""" if cls._lp_packagesets is None: cls._lp_packagesets = Launchpad.packagesets key = (sourcepackagename, distroseries, direct_inclusion) if key not in cls._source_sets: - params = { - 'sourcepackagename': sourcepackagename, - 'direct_inclusion': direct_inclusion, - } + params = {"sourcepackagename": sourcepackagename, "direct_inclusion": direct_inclusion} if distroseries is not None: - params['distroseries'] = distroseries._lpobject + params["distroseries"] = distroseries._lpobject - cls._source_sets[key] = [Packageset(packageset) for packageset in - cls._lp_packagesets.setsIncludingSource(**params)] + cls._source_sets[key] = [ + Packageset(packageset) + for packageset in cls._lp_packagesets.setsIncludingSource(**params) + ] return cls._source_sets[key] diff --git a/ubuntutools/lp/udtexceptions.py b/ubuntutools/lp/udtexceptions.py index 077502e..492695a 100644 --- a/ubuntutools/lp/udtexceptions.py +++ b/ubuntutools/lp/udtexceptions.py @@ -1,33 +1,40 @@ class PackageNotFoundException(BaseException): - """ Thrown when a package is not found """ + """Thrown when a package is not found""" + pass class SeriesNotFoundException(BaseException): - """ Thrown when a distroseries is not found """ + """Thrown when a distroseries is not found""" + pass class PocketDoesNotExistError(Exception): - '''Raised when a invalid pocket is used.''' + """Raised when a invalid pocket is used.""" + pass class ArchiveNotFoundException(BaseException): - """ Thrown when an archive for a distibution is not found """ + """Thrown when an archive for a distibution is not found""" + pass class AlreadyLoggedInError(Exception): - '''Raised when a second login is attempted.''' + """Raised when a second login is attempted.""" + pass class ArchSeriesNotFoundException(BaseException): """Thrown when a distroarchseries is not found.""" + pass class InvalidDistroValueError(ValueError): - """ Thrown when distro value is invalid """ + """Thrown when distro value is invalid""" + pass diff --git a/ubuntutools/misc.py b/ubuntutools/misc.py index dc29cd2..24a19ea 100644 --- a/ubuntutools/misc.py +++ b/ubuntutools/misc.py @@ -39,16 +39,17 @@ from urllib.parse import urlparse from ubuntutools.lp.udtexceptions import PocketDoesNotExistError import logging + Logger = logging.getLogger(__name__) -DEFAULT_POCKETS = ('Release', 'Security', 'Updates', 'Proposed') -POCKETS = DEFAULT_POCKETS + ('Backports',) +DEFAULT_POCKETS = ("Release", "Security", "Updates", "Proposed") +POCKETS = DEFAULT_POCKETS + ("Backports",) -DEFAULT_STATUSES = ('Pending', 'Published') -STATUSES = DEFAULT_STATUSES + ('Superseded', 'Deleted', 'Obsolete') +DEFAULT_STATUSES = ("Pending", "Published") +STATUSES = DEFAULT_STATUSES + ("Superseded", "Deleted", "Obsolete") -UPLOAD_QUEUE_STATUSES = ('New', 'Unapproved', 'Accepted', 'Done', 'Rejected') +UPLOAD_QUEUE_STATUSES = ("New", "Unapproved", "Accepted", "Done", "Rejected") DOWNLOAD_BLOCKSIZE_DEFAULT = 8192 @@ -66,7 +67,7 @@ class NotFoundError(DownloadError): def system_distribution_chain(): - """ system_distribution_chain() -> [string] + """system_distribution_chain() -> [string] Detect the system's distribution as well as all of its parent distributions and return them as a list of strings, with the @@ -77,18 +78,24 @@ def system_distribution_chain(): global _system_distribution_chain if len(_system_distribution_chain) == 0: try: - vendor = check_output(('dpkg-vendor', '--query', 'Vendor'), - encoding='utf-8').strip() + vendor = check_output(("dpkg-vendor", "--query", "Vendor"), encoding="utf-8").strip() _system_distribution_chain.append(vendor) except CalledProcessError: - Logger.error('Could not determine what distribution you are running.') + Logger.error("Could not determine what distribution you are running.") return [] while True: try: - parent = check_output(( - 'dpkg-vendor', '--vendor', _system_distribution_chain[-1], - '--query', 'Parent'), encoding='utf-8').strip() + parent = check_output( + ( + "dpkg-vendor", + "--vendor", + _system_distribution_chain[-1], + "--query", + "Parent", + ), + encoding="utf-8", + ).strip() except CalledProcessError: # Vendor has no parent break @@ -98,7 +105,7 @@ def system_distribution_chain(): def system_distribution(): - """ system_distro() -> string + """system_distro() -> string Detect the system's distribution and return it as a string. If the name of the distribution can't be determined, print an error message @@ -108,28 +115,26 @@ def system_distribution(): def host_architecture(): - """ host_architecture -> string + """host_architecture -> string Detect the host's architecture and return it as a string. If the architecture can't be determined, print an error message and return None. """ try: - arch = check_output(('dpkg', '--print-architecture'), - encoding='utf-8').strip() + arch = check_output(("dpkg", "--print-architecture"), encoding="utf-8").strip() except CalledProcessError: arch = None - if not arch or 'not found' in arch: - Logger.error('Not running on a Debian based system; ' - 'could not detect its architecture.') + if not arch or "not found" in arch: + Logger.error("Not running on a Debian based system; could not detect its architecture.") return None return arch def readlist(filename, uniq=True): - """ readlist(filename, uniq) -> list + """readlist(filename, uniq) -> list Read a list of words from the indicated file. If 'uniq' is True, filter out duplicated words. @@ -137,13 +142,13 @@ def readlist(filename, uniq=True): p = Path(filename) if not p.is_file(): - Logger.error(f'File {p} does not exist.') + Logger.error(f"File {p} does not exist.") return False - content = p.read_text().replace('\n', ' ').replace(',', ' ') + content = p.read_text().replace("\n", " ").replace(",", " ") if not content.strip(): - Logger.error(f'File {p} is empty.') + Logger.error(f"File {p} is empty.") return False items = [item for item in content.split() if item] @@ -154,21 +159,21 @@ def readlist(filename, uniq=True): return items -def split_release_pocket(release, default='Release'): - '''Splits the release and pocket name. +def split_release_pocket(release, default="Release"): + """Splits the release and pocket name. If the argument doesn't contain a pocket name then the 'Release' pocket is assumed. Returns the release and pocket name. - ''' + """ pocket = default if release is None: - raise ValueError('No release name specified') + raise ValueError("No release name specified") - if '-' in release: - (release, pocket) = release.rsplit('-', 1) + if "-" in release: + (release, pocket) = release.rsplit("-", 1) pocket = pocket.capitalize() if pocket not in POCKETS: @@ -178,18 +183,20 @@ def split_release_pocket(release, default='Release'): def require_utf8(): - '''Can be called by programs that only function in UTF-8 locales''' - if locale.getpreferredencoding() != 'UTF-8': + """Can be called by programs that only function in UTF-8 locales""" + if locale.getpreferredencoding() != "UTF-8": Logger.error("This program only functions in a UTF-8 locale. Aborting.") sys.exit(1) -_vendor_to_distroinfo = {"Debian": distro_info.DebianDistroInfo, - "Ubuntu": distro_info.UbuntuDistroInfo} +_vendor_to_distroinfo = { + "Debian": distro_info.DebianDistroInfo, + "Ubuntu": distro_info.UbuntuDistroInfo, +} def vendor_to_distroinfo(vendor): - """ vendor_to_distroinfo(string) -> DistroInfo class + """vendor_to_distroinfo(string) -> DistroInfo class Convert a string name of a distribution into a DistroInfo subclass representing that distribution, or None if the distribution is @@ -199,7 +206,7 @@ def vendor_to_distroinfo(vendor): def codename_to_distribution(codename): - """ codename_to_distribution(string) -> string + """codename_to_distribution(string) -> string Finds a given release codename in your distribution's genaology (i.e. looking at the current distribution and its parents), or @@ -215,7 +222,7 @@ def codename_to_distribution(codename): def verify_file_checksums(pathname, checksums={}, size=0): - """ verify checksums of file + """verify checksums of file Any failure will log an error. @@ -231,16 +238,16 @@ def verify_file_checksums(pathname, checksums={}, size=0): p = Path(pathname) if not p.is_file(): - Logger.error(f'File {p} not found') + Logger.error(f"File {p} not found") return False filesize = p.stat().st_size if size and size != filesize: - Logger.error(f'File {p} incorrect size, got {filesize} expected {size}') + Logger.error(f"File {p} incorrect size, got {filesize} expected {size}") return False for (alg, checksum) in checksums.items(): h = hashlib.new(alg) - with p.open('rb') as f: + with p.open("rb") as f: while True: block = f.read(h.block_size) if len(block) == 0: @@ -248,15 +255,15 @@ def verify_file_checksums(pathname, checksums={}, size=0): h.update(block) digest = h.hexdigest() if digest == checksum: - Logger.debug(f'File {p} checksum ({alg}) verified: {checksum}') + Logger.debug(f"File {p} checksum ({alg}) verified: {checksum}") else: - Logger.error(f'File {p} checksum ({alg}) mismatch: got {digest} expected {checksum}') + Logger.error(f"File {p} checksum ({alg}) mismatch: got {digest} expected {checksum}") return False return True def verify_file_checksum(pathname, alg, checksum, size=0): - """ verify checksum of file + """verify checksum of file pathname: str or Path full path to file @@ -273,7 +280,7 @@ def verify_file_checksum(pathname, alg, checksum, size=0): def extract_authentication(url): - """ Remove plaintext authentication data from a URL + """Remove plaintext authentication data from a URL If the URL has a username:password in its netloc, this removes it and returns the remaining URL, along with the username and password @@ -289,7 +296,7 @@ def extract_authentication(url): def download(src, dst, size=0, *, blocksize=DOWNLOAD_BLOCKSIZE_DEFAULT): - """ download/copy a file/url to local file + """download/copy a file/url to local file src: str or Path Source to copy from (file path or url) @@ -315,18 +322,18 @@ def download(src, dst, size=0, *, blocksize=DOWNLOAD_BLOCKSIZE_DEFAULT): dst = dst / Path(parsedsrc.path).name # Copy if src is a local file - if parsedsrc.scheme in ['', 'file']: + if parsedsrc.scheme in ["", "file"]: src = Path(parsedsrc.path).expanduser().resolve() if src != parsedsrc.path: - Logger.info(f'Parsed {parsedsrc.path} as {src}') + Logger.info(f"Parsed {parsedsrc.path} as {src}") if not src.exists(): - raise NotFoundError(f'Source file {src} not found') + raise NotFoundError(f"Source file {src} not found") if dst.exists(): if src.samefile(dst): - Logger.info(f'Using existing file {dst}') + Logger.info(f"Using existing file {dst}") return dst - Logger.info(f'Replacing existing file {dst}') - Logger.info(f'Copying file {src} to {dst}') + Logger.info(f"Replacing existing file {dst}") + Logger.info(f"Copying file {src} to {dst}") shutil.copyfile(src, dst) return dst @@ -334,18 +341,18 @@ def download(src, dst, size=0, *, blocksize=DOWNLOAD_BLOCKSIZE_DEFAULT): auth = (username, password) if username or password else None with tempfile.TemporaryDirectory() as d: - tmpdst = Path(d) / 'dst' + tmpdst = Path(d) / "dst" try: - with requests.get(src, stream=True, auth=auth) as fsrc, tmpdst.open('wb') as fdst: + with requests.get(src, stream=True, auth=auth) as fsrc, tmpdst.open("wb") as fdst: fsrc.raise_for_status() _download(fsrc, fdst, size, blocksize=blocksize) except requests.exceptions.HTTPError as e: if e.response is not None and e.response.status_code == 404: - raise NotFoundError(f'URL {src} not found: {e}') + raise NotFoundError(f"URL {src} not found: {e}") raise DownloadError(e) except requests.exceptions.ConnectionError as e: # This is likely a archive hostname that doesn't resolve, like 'ftpmaster.internal' - raise NotFoundError(f'URL {src} not found: {e}') + raise NotFoundError(f"URL {src} not found: {e}") except requests.exceptions.RequestException as e: raise DownloadError(e) shutil.move(tmpdst, dst) @@ -358,60 +365,64 @@ class _StderrProgressBar(object): def __init__(self, max_width): self.full_width = min(max_width, self.BAR_WIDTH_DEFAULT) - self.width = self.full_width - len('[] 99%') + self.width = self.full_width - len("[] 99%") self.show_progress = self.full_width >= self.BAR_WIDTH_MIN def update(self, progress, total): if not self.show_progress: return pct = progress * 100 // total - pctstr = f'{pct:>3}%' + pctstr = f"{pct:>3}%" barlen = self.width * pct // 100 - barstr = '=' * barlen - barstr = barstr[:-1] + '>' + barstr = "=" * barlen + barstr = barstr[:-1] + ">" barstr = barstr.ljust(self.width) - fullstr = f'\r[{barstr}]{pctstr}' + fullstr = f"\r[{barstr}]{pctstr}" sys.stderr.write(fullstr) sys.stderr.flush() def finish(self): if not self.show_progress: return - sys.stderr.write('\n') + sys.stderr.write("\n") sys.stderr.flush() def _download(fsrc, fdst, size, *, blocksize): - """ helper method to download src to dst using requests library. """ + """helper method to download src to dst using requests library.""" url = fsrc.url - Logger.debug(f'Using URL: {url}') + Logger.debug(f"Using URL: {url}") if not size: with suppress(AttributeError, TypeError, ValueError): - size = int(fsrc.headers.get('Content-Length')) + size = int(fsrc.headers.get("Content-Length")) parsed = urlparse(url) filename = Path(parsed.path).name hostname = parsed.hostname - sizemb = ' (%0.3f MiB)' % (size / 1024.0 / 1024) if size else '' - Logger.info(f'Downloading {filename} from {hostname}{sizemb}') + sizemb = " (%0.3f MiB)" % (size / 1024.0 / 1024) if size else "" + Logger.info(f"Downloading {filename} from {hostname}{sizemb}") # Don't show progress if: # logging INFO is suppressed # stderr isn't a tty # we don't know the total file size # the file is content-encoded (i.e. compressed) - show_progress = all((Logger.isEnabledFor(logging.INFO), - sys.stderr.isatty(), - size > 0, - 'Content-Encoding' not in fsrc.headers)) + show_progress = all( + ( + Logger.isEnabledFor(logging.INFO), + sys.stderr.isatty(), + size > 0, + "Content-Encoding" not in fsrc.headers, + ) + ) terminal_width = 0 if show_progress: try: terminal_width = os.get_terminal_size(sys.stderr.fileno()).columns except Exception as e: - Logger.error(f'Error finding stderr width, suppressing progress bar: {e}') + Logger.error(f"Error finding stderr width, suppressing progress bar: {e}") progress_bar = _StderrProgressBar(max_width=terminal_width) downloaded = 0 @@ -423,20 +434,21 @@ def _download(fsrc, fdst, size, *, blocksize): finally: progress_bar.finish() if size and size > downloaded: - Logger.error('Partial download: %0.3f MiB of %0.3f MiB' % - (downloaded / 1024.0 / 1024, - size / 1024.0 / 1024)) + Logger.error( + "Partial download: %0.3f MiB of %0.3f MiB" + % (downloaded / 1024.0 / 1024, size / 1024.0 / 1024) + ) def _download_text(src, binary, *, blocksize): with tempfile.TemporaryDirectory() as d: - dst = Path(d) / 'dst' + dst = Path(d) / "dst" download(src, dst, blocksize=blocksize) return dst.read_bytes() if binary else dst.read_text() def download_text(src, mode=None, *, blocksize=DOWNLOAD_BLOCKSIZE_DEFAULT): - """ Return the text content of a downloaded file + """Return the text content of a downloaded file src: str or Path Source to copy from (file path or url) @@ -449,9 +461,9 @@ def download_text(src, mode=None, *, blocksize=DOWNLOAD_BLOCKSIZE_DEFAULT): Returns text content of downloaded file """ - return _download_text(src, binary='b' in (mode or ''), blocksize=blocksize) + return _download_text(src, binary="b" in (mode or ""), blocksize=blocksize) def download_bytes(src, *, blocksize=DOWNLOAD_BLOCKSIZE_DEFAULT): - """ Same as download_text() but returns bytes """ + """Same as download_text() but returns bytes""" return _download_text(src, binary=True, blocksize=blocksize) diff --git a/ubuntutools/pullpkg.py b/ubuntutools/pullpkg.py index 206cb68..44b4569 100644 --- a/ubuntutools/pullpkg.py +++ b/ubuntutools/pullpkg.py @@ -34,42 +34,50 @@ from distro_info import DebianDistroInfo from urllib.parse import urlparse -from ubuntutools.archive import (UbuntuSourcePackage, DebianSourcePackage, - UbuntuCloudArchiveSourcePackage, - PersonalPackageArchiveSourcePackage) +from ubuntutools.archive import ( + UbuntuSourcePackage, + DebianSourcePackage, + UbuntuCloudArchiveSourcePackage, + PersonalPackageArchiveSourcePackage, +) from ubuntutools.config import UDTConfig -from ubuntutools.lp.lpapicache import (Distribution, Launchpad) -from ubuntutools.lp.udtexceptions import (AlreadyLoggedInError, - SeriesNotFoundException, - PackageNotFoundException, - PocketDoesNotExistError, - InvalidDistroValueError) -from ubuntutools.misc import (split_release_pocket, - host_architecture, - download, - UPLOAD_QUEUE_STATUSES, - STATUSES) +from ubuntutools.lp.lpapicache import Distribution, Launchpad +from ubuntutools.lp.udtexceptions import ( + AlreadyLoggedInError, + SeriesNotFoundException, + PackageNotFoundException, + PocketDoesNotExistError, + InvalidDistroValueError, +) +from ubuntutools.misc import ( + split_release_pocket, + host_architecture, + download, + UPLOAD_QUEUE_STATUSES, + STATUSES, +) # by default we use standard logging.getLogger() and only use # ubuntutools.getLogger() in PullPkg().main() from ubuntutools import getLogger as ubuntutools_getLogger import logging + Logger = logging.getLogger(__name__) -PULL_SOURCE = 'source' -PULL_DEBS = 'debs' -PULL_DDEBS = 'ddebs' -PULL_UDEBS = 'udebs' -PULL_LIST = 'list' +PULL_SOURCE = "source" +PULL_DEBS = "debs" +PULL_DDEBS = "ddebs" +PULL_UDEBS = "udebs" +PULL_LIST = "list" VALID_PULLS = [PULL_SOURCE, PULL_DEBS, PULL_DDEBS, PULL_UDEBS, PULL_LIST] VALID_BINARY_PULLS = [PULL_DEBS, PULL_DDEBS, PULL_UDEBS] -DISTRO_DEBIAN = 'debian' -DISTRO_UBUNTU = 'ubuntu' -DISTRO_UCA = 'uca' -DISTRO_PPA = 'ppa' +DISTRO_DEBIAN = "debian" +DISTRO_UBUNTU = "ubuntu" +DISTRO_UCA = "uca" +DISTRO_PPA = "ppa" DISTRO_PKG_CLASS = { DISTRO_DEBIAN: DebianSourcePackage, @@ -81,12 +89,14 @@ VALID_DISTROS = DISTRO_PKG_CLASS.keys() class InvalidPullValueError(ValueError): - """ Thrown when --pull value is invalid """ + """Thrown when --pull value is invalid""" + pass class PullPkg(object): """Class used to pull file(s) associated with a specific package""" + @classmethod def main(cls, *args, **kwargs): """For use by stand-alone cmdline scripts. @@ -107,53 +117,67 @@ class PullPkg(object): cls(*args, **kwargs).pull() return except KeyboardInterrupt: - Logger.info('User abort.') - except (PackageNotFoundException, SeriesNotFoundException, - PocketDoesNotExistError, InvalidDistroValueError, - InvalidPullValueError) as e: + Logger.info("User abort.") + except ( + PackageNotFoundException, + SeriesNotFoundException, + PocketDoesNotExistError, + InvalidDistroValueError, + InvalidPullValueError, + ) as e: Logger.error(str(e)) sys.exit(errno.ENOENT) def __init__(self, *args, **kwargs): - self._default_pull = kwargs.get('pull') - self._default_distro = kwargs.get('distro') - self._default_arch = kwargs.get('arch', host_architecture()) + self._default_pull = kwargs.get("pull") + self._default_distro = kwargs.get("distro") + self._default_arch = kwargs.get("arch", host_architecture()) def parse_args(self, args): args = args[:] help_default_pull = "What to pull: " + ", ".join(VALID_PULLS) if self._default_pull: - help_default_pull += (" (default: %s)" % self._default_pull) + help_default_pull += " (default: %s)" % self._default_pull help_default_distro = "Pull from: " + ", ".join(VALID_DISTROS) if self._default_distro: - help_default_distro += (" (default: %s)" % self._default_distro) - help_default_arch = ("Get binary packages for arch") - help_default_arch += ("(default: %s)" % self._default_arch) + help_default_distro += " (default: %s)" % self._default_distro + help_default_arch = "Get binary packages for arch" + help_default_arch += "(default: %s)" % self._default_arch # use add_help=False because we do parse_known_args() below, and if # that sees --help then it exits immediately parser = ArgumentParser(add_help=False) - parser.add_argument('-L', '--login', action='store_true', - help="Login to Launchpad") - parser.add_argument('-v', '--verbose', action='count', default=0, - help="Increase verbosity/debug") - parser.add_argument('-d', '--download-only', action='store_true', - help="Do not extract the source package") - parser.add_argument('-m', '--mirror', action='append', - help='Preferred mirror(s)') - parser.add_argument('--no-conf', action='store_true', - help="Don't read config files or environment variables") - parser.add_argument('--no-verify-signature', action='store_true', - help="Don't fail if dsc signature can't be verified") - parser.add_argument('-s', '--status', action='append', default=[], - help="Search for packages with specific status(es)") - parser.add_argument('-a', '--arch', default=self._default_arch, - help=help_default_arch) - parser.add_argument('-p', '--pull', default=self._default_pull, - help=help_default_pull) - parser.add_argument('-D', '--distro', default=self._default_distro, - help=help_default_distro) + parser.add_argument("-L", "--login", action="store_true", help="Login to Launchpad") + parser.add_argument( + "-v", "--verbose", action="count", default=0, help="Increase verbosity/debug" + ) + parser.add_argument( + "-d", "--download-only", action="store_true", help="Do not extract the source package" + ) + parser.add_argument("-m", "--mirror", action="append", help="Preferred mirror(s)") + parser.add_argument( + "--no-conf", + action="store_true", + help="Don't read config files or environment variables", + ) + parser.add_argument( + "--no-verify-signature", + action="store_true", + help="Don't fail if dsc signature can't be verified", + ) + parser.add_argument( + "-s", + "--status", + action="append", + default=[], + help="Search for packages with specific status(es)", + ) + parser.add_argument("-a", "--arch", default=self._default_arch, help=help_default_arch) + parser.add_argument("-p", "--pull", default=self._default_pull, help=help_default_pull) + parser.add_argument( + "-D", "--distro", default=self._default_distro, help=help_default_distro + ) # add distro-specific params try: @@ -163,30 +187,36 @@ class PullPkg(object): distro = None if distro == DISTRO_UBUNTU: - parser.add_argument('--security', action='store_true', - help='Pull from the Ubuntu Security Team (proposed) PPA') - parser.add_argument('--upload-queue', action='store_true', - help='Pull from the Ubuntu upload queue') + parser.add_argument( + "--security", + action="store_true", + help="Pull from the Ubuntu Security Team (proposed) PPA", + ) + parser.add_argument( + "--upload-queue", action="store_true", help="Pull from the Ubuntu upload queue" + ) if distro == DISTRO_PPA: - parser.add_argument('--ppa', help='PPA to pull from') + parser.add_argument("--ppa", help="PPA to pull from") if parser.parse_known_args(args)[0].ppa is None: # check for any param starting with "ppa:" # if found, move it to a --ppa param for param in args: - if param.startswith('ppa:'): + if param.startswith("ppa:"): args.remove(param) args.insert(0, param) - args.insert(0, '--ppa') + args.insert(0, "--ppa") break # add the positional params - parser.add_argument('package', help="Package name to pull") - parser.add_argument('release', nargs='?', help="Release to pull from") - parser.add_argument('version', nargs='?', help="Package version to pull") + parser.add_argument("package", help="Package name to pull") + parser.add_argument("release", nargs="?", help="Release to pull from") + parser.add_argument("version", nargs="?", help="Package version to pull") - epilog = ("Note on --status: if a version is provided, all status types " - "will be searched; if no version is provided, by default only " - "'Pending' and 'Published' status will be searched.") + epilog = ( + "Note on --status: if a version is provided, all status types " + "will be searched; if no version is provided, by default only " + "'Pending' and 'Published' status will be searched." + ) # since parser has no --help handler, create a new parser that does newparser = ArgumentParser(parents=[parser], epilog=epilog) @@ -198,11 +228,11 @@ class PullPkg(object): raise InvalidPullValueError("Must specify --pull") # allow 'dbgsym' as alias for 'ddebs' - if pull == 'dbgsym': + if pull == "dbgsym": Logger.debug("Pulling '%s' for '%s'", PULL_DDEBS, pull) pull = PULL_DDEBS # assume anything starting with 'bin' means 'debs' - if str(pull).startswith('bin'): + if str(pull).startswith("bin"): Logger.debug("Pulling '%s' for '%s'", PULL_DEBS, pull) pull = PULL_DEBS # verify pull action is valid @@ -218,11 +248,11 @@ class PullPkg(object): distro = distro.lower() # allow 'lp' for 'ubuntu' - if distro == 'lp': + if distro == "lp": Logger.debug("Using distro '%s' for '%s'", DISTRO_UBUNTU, distro) distro = DISTRO_UBUNTU # assume anything with 'cloud' is UCA - if re.match(r'.*cloud.*', distro): + if re.match(r".*cloud.*", distro): Logger.debug("Using distro '%s' for '%s'", DISTRO_UCA, distro) distro = DISTRO_UCA # verify distro is valid @@ -256,8 +286,7 @@ class PullPkg(object): # let SeriesNotFoundException flow up d.getSeries(release) - Logger.debug("Using distro '%s' release '%s' pocket '%s'", - distro, release, pocket) + Logger.debug("Using distro '%s' release '%s' pocket '%s'", distro, release, pocket) return (release, pocket) def parse_release_and_version(self, distro, release, version, try_swap=True): @@ -281,95 +310,99 @@ class PullPkg(object): # they should all be provided, though the optional ones may be None # type bool - assert 'verbose' in options - assert 'download_only' in options - assert 'no_conf' in options - assert 'no_verify_signature' in options - assert 'status' in options + assert "verbose" in options + assert "download_only" in options + assert "no_conf" in options + assert "no_verify_signature" in options + assert "status" in options # type string - assert 'pull' in options - assert 'distro' in options - assert 'arch' in options - assert 'package' in options + assert "pull" in options + assert "distro" in options + assert "arch" in options + assert "package" in options # type string, optional - assert 'release' in options - assert 'version' in options + assert "release" in options + assert "version" in options # type list of strings, optional - assert 'mirror' in options + assert "mirror" in options - options['pull'] = self.parse_pull(options['pull']) - options['distro'] = self.parse_distro(options['distro']) + options["pull"] = self.parse_pull(options["pull"]) + options["distro"] = self.parse_distro(options["distro"]) # ensure these are always included so we can just check for None/False later - options['ppa'] = options.get('ppa', None) - options['security'] = options.get('security', False) - options['upload_queue'] = options.get('upload_queue', False) + options["ppa"] = options.get("ppa", None) + options["security"] = options.get("security", False) + options["upload_queue"] = options.get("upload_queue", False) return options def _get_params(self, options): - distro = options['distro'] - pull = options['pull'] + distro = options["distro"] + pull = options["pull"] params = {} - params['package'] = options['package'] + params["package"] = options["package"] - if options['release']: - (r, v, p) = self.parse_release_and_version(distro, options['release'], - options['version']) - params['series'] = r - params['version'] = v - params['pocket'] = p + if options["release"]: + (r, v, p) = self.parse_release_and_version( + distro, options["release"], options["version"] + ) + params["series"] = r + params["version"] = v + params["pocket"] = p - if (params['package'].endswith('.dsc') and not params['series'] and not params['version']): - params['dscfile'] = params['package'] - params.pop('package') + if params["package"].endswith(".dsc") and not params["series"] and not params["version"]: + params["dscfile"] = params["package"] + params.pop("package") - if options['security']: - if options['ppa']: - Logger.warning('Both --security and --ppa specified, ignoring --ppa') - Logger.debug('Checking Ubuntu Security PPA') + if options["security"]: + if options["ppa"]: + Logger.warning("Both --security and --ppa specified, ignoring --ppa") + Logger.debug("Checking Ubuntu Security PPA") # --security is just a shortcut for --ppa ppa:ubuntu-security-proposed/ppa - options['ppa'] = 'ubuntu-security-proposed/ppa' + options["ppa"] = "ubuntu-security-proposed/ppa" - if options['ppa']: - if options['ppa'].startswith('ppa:'): - params['ppa'] = options['ppa'][4:] + if options["ppa"]: + if options["ppa"].startswith("ppa:"): + params["ppa"] = options["ppa"][4:] else: - params['ppa'] = options['ppa'] + params["ppa"] = options["ppa"] elif distro == DISTRO_PPA: - raise ValueError('Must specify PPA to pull from') + raise ValueError("Must specify PPA to pull from") mirrors = [] - if options['mirror']: - mirrors.extend(options['mirror']) + if options["mirror"]: + mirrors.extend(options["mirror"]) if pull == PULL_DDEBS: - config = UDTConfig(options['no_conf']) - ddebs_mirror = config.get_value(distro.upper() + '_DDEBS_MIRROR') + config = UDTConfig(options["no_conf"]) + ddebs_mirror = config.get_value(distro.upper() + "_DDEBS_MIRROR") if ddebs_mirror: mirrors.append(ddebs_mirror) if mirrors: Logger.debug("using mirrors %s", ", ".join(mirrors)) - params['mirrors'] = mirrors + params["mirrors"] = mirrors - params['verify_signature'] = not options['no_verify_signature'] + params["verify_signature"] = not options["no_verify_signature"] - params['status'] = STATUSES if 'all' in options['status'] else options['status'] + params["status"] = STATUSES if "all" in options["status"] else options["status"] # special handling for upload queue - if options['upload_queue']: - if len(options['status']) > 1: - raise ValueError("Too many --status provided, " - "can only search for a single status or 'all'") - if not options['status']: - params['status'] = None - elif options['status'][0].lower() == 'all': - params['status'] = 'all' - elif options['status'][0].capitalize() in UPLOAD_QUEUE_STATUSES: - params['status'] = options['status'][0].capitalize() + if options["upload_queue"]: + if len(options["status"]) > 1: + raise ValueError( + "Too many --status provided, can only search for a single status or 'all'" + ) + if not options["status"]: + params["status"] = None + elif options["status"][0].lower() == "all": + params["status"] = "all" + elif options["status"][0].capitalize() in UPLOAD_QUEUE_STATUSES: + params["status"] = options["status"][0].capitalize() else: - msg = ("Invalid upload queue status '%s': valid values are %s" % - (options['status'][0], ', '.join(UPLOAD_QUEUE_STATUSES))) + msg = "Invalid upload queue status '%s': valid values are %s" % ( + options["status"][0], + ", ".join(UPLOAD_QUEUE_STATUSES), + ) raise ValueError(msg) return params @@ -378,56 +411,58 @@ class PullPkg(object): """Pull (download) specified package file(s)""" options = self.parse_args(args) - if options['verbose']: + if options["verbose"]: Logger.setLevel(logging.DEBUG) - if options['verbose'] > 1: + if options["verbose"] > 1: logging.getLogger(__package__).setLevel(logging.DEBUG) Logger.debug("pullpkg options: %s", options) - pull = options['pull'] - distro = options['distro'] + pull = options["pull"] + distro = options["distro"] - if options['login']: + if options["login"]: Logger.debug("Logging in to Launchpad:") try: Launchpad.login() except AlreadyLoggedInError: - Logger.error("Launchpad singleton has already performed a login, " - "and its design prevents another login") + Logger.error( + "Launchpad singleton has already performed a login, " + "and its design prevents another login" + ) Logger.warning("Continuing anyway, with existing Launchpad instance") params = self._get_params(options) - package = params['package'] + package = params["package"] - if options['upload_queue']: + if options["upload_queue"]: # upload queue API is different/simpler - self.pull_upload_queue(pull, arch=options['arch'], - download_only=options['download_only'], - **params) + self.pull_upload_queue( + pull, arch=options["arch"], download_only=options["download_only"], **params + ) return # call implementation, and allow exceptions to flow up to caller srcpkg = DISTRO_PKG_CLASS[distro](**params) spph = srcpkg.lp_spph - Logger.info('Found %s', spph.display_name) + Logger.info("Found %s", spph.display_name) if pull == PULL_LIST: Logger.info("Source files:") - for f in srcpkg.dsc['Files']: - Logger.info(" %s", f['name']) + for f in srcpkg.dsc["Files"]: + Logger.info(" %s", f["name"]) Logger.info("Binary files:") - for f in spph.getBinaries(options['arch']): - archtext = '' + for f in spph.getBinaries(options["arch"]): + archtext = "" name = f.getFileName() - if name.rpartition('.')[0].endswith('all'): + if name.rpartition(".")[0].endswith("all"): archtext = f" ({f.arch})" Logger.info(f" {name}{archtext}") elif pull == PULL_SOURCE: # allow DownloadError to flow up to caller srcpkg.pull() - if options['download_only']: + if options["download_only"]: Logger.debug("--download-only specified, not extracting") else: srcpkg.unpack() @@ -435,70 +470,86 @@ class PullPkg(object): name = None if package != spph.getPackageName(): Logger.info("Pulling only binary package '%s'", package) - Logger.info("Use package name '%s' to pull all binary packages", - spph.getPackageName()) + Logger.info( + "Use package name '%s' to pull all binary packages", spph.getPackageName() + ) name = package # e.g. 'debs' -> 'deb' - ext = pull.rstrip('s') + ext = pull.rstrip("s") if distro == DISTRO_DEBIAN: # Debian ddebs don't use .ddeb extension, unfortunately :( if pull in [PULL_DEBS, PULL_DDEBS]: - name = name or '.*' - ext = 'deb' + name = name or ".*" + ext = "deb" if pull == PULL_DEBS: - name += r'(?>>.*<<<$', re.UNICODE),) + placeholders = (re.compile(r"^>>>.*<<<$", re.UNICODE),) self.placeholders = placeholders def edit(self, optional=False): if optional: print("\n\nCurrently the %s looks like:" % self.description) - with open(self.filename, 'r', encoding='utf-8') as f: + with open(self.filename, "r", encoding="utf-8") as f: print(f.read()) if YesNoQuestion().ask("Edit", "no") == "no": return @@ -135,21 +135,22 @@ class EditFile(object): done = False while not done: old_mtime = os.stat(self.filename).st_mtime - subprocess.check_call(['sensible-editor', self.filename]) + subprocess.check_call(["sensible-editor", self.filename]) modified = old_mtime != os.stat(self.filename).st_mtime placeholders_present = False if self.placeholders: - with open(self.filename, 'r', encoding='utf-8') as f: + with open(self.filename, "r", encoding="utf-8") as f: for line in f: for placeholder in self.placeholders: if placeholder.search(line.strip()): placeholders_present = True if placeholders_present: - print("Placeholders still present in the %s. " - "Please replace them with useful information." - % self.description) - confirmation_prompt(action='edit again') + print( + "Placeholders still present in the %s. " + "Please replace them with useful information." % self.description + ) + confirmation_prompt(action="edit again") elif not modified: print("The %s was not modified" % self.description) if YesNoQuestion().ask("Edit again", "yes") == "no": @@ -158,45 +159,44 @@ class EditFile(object): done = True def check_edit(self): - '''Override this to implement extra checks on the edited report. + """Override this to implement extra checks on the edited report. Should return False if another round of editing is needed, and should prompt the user to confirm that, if necessary. - ''' + """ return True class EditBugReport(EditFile): - split_re = re.compile(r'^Summary.*?:\s+(.*?)\s+' - r'Description:\s+(.*)$', - re.DOTALL | re.UNICODE) + split_re = re.compile(r"^Summary.*?:\s+(.*?)\s+Description:\s+(.*)$", re.DOTALL | re.UNICODE) def __init__(self, subject, body, placeholders=None): - prefix = os.path.basename(sys.argv[0]) + '_' - tmpfile = tempfile.NamedTemporaryFile(prefix=prefix, suffix='.txt', - delete=False) - tmpfile.write((u'Summary (one line):\n%s\n\nDescription:\n%s' - % (subject, body)).encode('utf-8')) + prefix = os.path.basename(sys.argv[0]) + "_" + tmpfile = tempfile.NamedTemporaryFile(prefix=prefix, suffix=".txt", delete=False) + tmpfile.write( + ("Summary (one line):\n%s\n\nDescription:\n%s" % (subject, body)).encode("utf-8") + ) tmpfile.close() - super(EditBugReport, self).__init__(tmpfile.name, 'bug report', - placeholders) + super(EditBugReport, self).__init__(tmpfile.name, "bug report", placeholders) def check_edit(self): - with open(self.filename, 'r', encoding='utf-8') as f: + with open(self.filename, "r", encoding="utf-8") as f: report = f.read() if self.split_re.match(report) is None: - print("The %s doesn't start with 'Summary:' and 'Description:' " - "blocks" % self.description) - confirmation_prompt('edit again') + print( + "The %s doesn't start with 'Summary:' and 'Description:' " + "blocks" % self.description + ) + confirmation_prompt("edit again") return False return True def get_report(self): - with open(self.filename, 'r', encoding='utf-8') as f: + with open(self.filename, "r", encoding="utf-8") as f: report = f.read() match = self.split_re.match(report) - title = match.group(1).replace(u'\n', u' ') + title = match.group(1).replace("\n", " ") report = (title, match.group(2)) os.unlink(self.filename) return report diff --git a/ubuntutools/rdepends.py b/ubuntutools/rdepends.py index 4718ce7..88c198a 100644 --- a/ubuntutools/rdepends.py +++ b/ubuntutools/rdepends.py @@ -22,13 +22,12 @@ class RDependsException(Exception): pass -def query_rdepends(package, release, arch, - server='http://qa.ubuntuwire.org/rdepends'): +def query_rdepends(package, release, arch, server="http://qa.ubuntuwire.org/rdepends"): """Look up a packages reverse-dependencies on the Ubuntuwire Reverse- webservice """ - url = os.path.join(server, 'v1', release, arch, package) + url = os.path.join(server, "v1", release, arch, package) response, data = httplib2.Http().request(url) if response.status != 200: diff --git a/ubuntutools/requestsync/lp.py b/ubuntutools/requestsync/lp.py index 758958b..64ced68 100644 --- a/ubuntutools/requestsync/lp.py +++ b/ubuntutools/requestsync/lp.py @@ -27,16 +27,21 @@ from distro_info import DebianDistroInfo, DistroDataOutdated from httplib2 import Http, HttpLib2Error from ubuntutools.lp import udtexceptions -from ubuntutools.lp.lpapicache import (Launchpad, Distribution, PersonTeam, - DistributionSourcePackage) +from ubuntutools.lp.lpapicache import ( + Launchpad, + Distribution, + PersonTeam, + DistributionSourcePackage, +) from ubuntutools.question import confirmation_prompt import logging + Logger = logging.getLogger(__name__) def get_debian_srcpkg(name, release): - debian = Distribution('debian') + debian = Distribution("debian") debian_archive = debian.getArchive() try: @@ -47,82 +52,86 @@ def get_debian_srcpkg(name, release): return debian_archive.getSourcePackage(name, release) -def get_ubuntu_srcpkg(name, release, pocket='Release'): - ubuntu = Distribution('ubuntu') +def get_ubuntu_srcpkg(name, release, pocket="Release"): + ubuntu = Distribution("ubuntu") ubuntu_archive = ubuntu.getArchive() try: return ubuntu_archive.getSourcePackage(name, release, pocket) except udtexceptions.PackageNotFoundException: - if pocket != 'Release': - parent_pocket = 'Release' - if pocket == 'Updates': - parent_pocket = 'Proposed' + if pocket != "Release": + parent_pocket = "Release" + if pocket == "Updates": + parent_pocket = "Proposed" return get_ubuntu_srcpkg(name, release, parent_pocket) raise def need_sponsorship(name, component, release): - ''' + """ Check if the user has upload permissions for either the package itself or the component - ''' - archive = Distribution('ubuntu').getArchive() - distroseries = Distribution('ubuntu').getSeries(release) + """ + archive = Distribution("ubuntu").getArchive() + distroseries = Distribution("ubuntu").getSeries(release) - need_sponsor = not PersonTeam.me.canUploadPackage(archive, distroseries, - name, component) + need_sponsor = not PersonTeam.me.canUploadPackage(archive, distroseries, name, component) if need_sponsor: - print('''You are not able to upload this package directly to Ubuntu. + print( + """You are not able to upload this package directly to Ubuntu. Your sync request shall require an approval by a member of the appropriate sponsorship team, who shall be subscribed to this bug report. This must be done before it can be processed by a member of the Ubuntu Archive -team.''') +team.""" + ) confirmation_prompt() return need_sponsor def check_existing_reports(srcpkg): - ''' + """ Check existing bug reports on Launchpad for a possible sync request. If found ask for confirmation on filing a request. - ''' + """ # Fetch the package's bug list from Launchpad - pkg = Distribution('ubuntu').getSourcePackage(name=srcpkg) - pkg_bug_list = pkg.searchTasks(status=["Incomplete", "New", "Confirmed", - "Triaged", "In Progress", - "Fix Committed"], - omit_duplicates=True) + pkg = Distribution("ubuntu").getSourcePackage(name=srcpkg) + pkg_bug_list = pkg.searchTasks( + status=["Incomplete", "New", "Confirmed", "Triaged", "In Progress", "Fix Committed"], + omit_duplicates=True, + ) # Search bug list for other sync requests. for bug in pkg_bug_list: # check for Sync or sync and the package name - if not bug.is_complete and 'ync %s' % srcpkg in bug.title: - print('The following bug could be a possible duplicate sync bug ' - 'on Launchpad:\n' - ' * %s (%s)\n' - 'Please check the above URL to verify this before ' - 'continuing.' - % (bug.title, bug.web_link)) + if not bug.is_complete and "ync %s" % srcpkg in bug.title: + print( + "The following bug could be a possible duplicate sync bug " + "on Launchpad:\n" + " * %s (%s)\n" + "Please check the above URL to verify this before " + "continuing." % (bug.title, bug.web_link) + ) confirmation_prompt() def get_ubuntu_delta_changelog(srcpkg): - ''' + """ Download the Ubuntu changelog and extract the entries since the last sync from Debian. - ''' - archive = Distribution('ubuntu').getArchive() - spph = archive.getPublishedSources(source_name=srcpkg.getPackageName(), - exact_match=True, pocket='Release') + """ + archive = Distribution("ubuntu").getArchive() + spph = archive.getPublishedSources( + source_name=srcpkg.getPackageName(), exact_match=True, pocket="Release" + ) debian_info = DebianDistroInfo() - topline = re.compile(r'^(\w%(name_chars)s*) \(([^\(\) \t]+)\)' - r'((\s+%(name_chars)s+)+)\;' - % {'name_chars': '[-+0-9a-z.]'}, - re.IGNORECASE) + topline = re.compile( + r"^(\w%(name_chars)s*) \(([^\(\) \t]+)\)" + r"((\s+%(name_chars)s+)+)\;" % {"name_chars": "[-+0-9a-z.]"}, + re.IGNORECASE, + ) delta = [] for record in spph: changes_url = record.changesFileUrl() @@ -135,56 +144,53 @@ def get_ubuntu_delta_changelog(srcpkg): Logger.error(str(e)) break if response.status != 200: - Logger.error("%s: %s %s", changes_url, response.status, - response.reason) + Logger.error("%s: %s %s", changes_url, response.status, response.reason) break changes = Changes(Http().request(changes_url)[1]) - for line in changes['Changes'].splitlines(): + for line in changes["Changes"].splitlines(): line = line[1:] m = topline.match(line) if m: - distribution = m.group(3).split()[0].split('-')[0] + distribution = m.group(3).split()[0].split("-")[0] if debian_info.valid(distribution): break - if line.startswith(u' '): + if line.startswith(" "): delta.append(line) else: continue break - return '\n'.join(delta) + return "\n".join(delta) def post_bug(srcpkg, subscribe, status, bugtitle, bugtext): - ''' + """ Use the LP API to file the sync request. - ''' + """ - print('The final report is:\nSummary: %s\nDescription:\n%s\n' - % (bugtitle, bugtext)) + print("The final report is:\nSummary: %s\nDescription:\n%s\n" % (bugtitle, bugtext)) confirmation_prompt() if srcpkg: bug_target = DistributionSourcePackage( - '%subuntu/+source/%s' % (Launchpad._root_uri, srcpkg)) + "%subuntu/+source/%s" % (Launchpad._root_uri, srcpkg) + ) else: # new source package - bug_target = Distribution('ubuntu') + bug_target = Distribution("ubuntu") # create bug - bug = Launchpad.bugs.createBug(title=bugtitle, description=bugtext, - target=bug_target()) + bug = Launchpad.bugs.createBug(title=bugtitle, description=bugtext, target=bug_target()) # newly created bugreports have only one task task = bug.bug_tasks[0] # only members of ubuntu-bugcontrol can set importance - if PersonTeam.me.isLpTeamMember('ubuntu-bugcontrol'): - task.importance = 'Wishlist' + if PersonTeam.me.isLpTeamMember("ubuntu-bugcontrol"): + task.importance = "Wishlist" task.status = status task.lp_save() bug.subscribe(person=PersonTeam(subscribe)()) - print('Sync request filed as bug #%i: %s' - % (bug.id, bug.web_link)) + print("Sync request filed as bug #%i: %s" % (bug.id, bug.web_link)) diff --git a/ubuntutools/requestsync/mail.py b/ubuntutools/requestsync/mail.py index 4706bc9..f5e53f4 100644 --- a/ubuntutools/requestsync/mail.py +++ b/ubuntutools/requestsync/mail.py @@ -36,16 +36,17 @@ from ubuntutools.lp.udtexceptions import PackageNotFoundException from ubuntutools.question import confirmation_prompt, YesNoQuestion import logging + Logger = logging.getLogger(__name__) __all__ = [ - 'get_debian_srcpkg', - 'get_ubuntu_srcpkg', - 'need_sponsorship', - 'check_existing_reports', - 'get_ubuntu_delta_changelog', - 'mail_bug', + "get_debian_srcpkg", + "get_ubuntu_srcpkg", + "need_sponsorship", + "check_existing_reports", + "get_ubuntu_delta_changelog", + "mail_bug", ] @@ -67,73 +68,91 @@ def get_ubuntu_srcpkg(name, release): def need_sponsorship(name, component, release): - ''' + """ Ask the user if he has upload permissions for the package or the component. - ''' + """ - val = YesNoQuestion().ask("Do you have upload permissions for the '%s' component or " - "the package '%s' in Ubuntu %s?\nIf in doubt answer 'n'." % - (component, name, release), 'no') - return val == 'no' + val = YesNoQuestion().ask( + "Do you have upload permissions for the '%s' component or " + "the package '%s' in Ubuntu %s?\nIf in doubt answer 'n'." % (component, name, release), + "no", + ) + return val == "no" def check_existing_reports(srcpkg): - ''' + """ Point the user to the URL to manually check for duplicate bug reports. - ''' - print('Please check on ' - 'https://bugs.launchpad.net/ubuntu/+source/%s/+bugs\n' - 'for duplicate sync requests before continuing.' % srcpkg) + """ + print( + "Please check on " + "https://bugs.launchpad.net/ubuntu/+source/%s/+bugs\n" + "for duplicate sync requests before continuing." % srcpkg + ) confirmation_prompt() def get_ubuntu_delta_changelog(srcpkg): - ''' + """ Download the Ubuntu changelog and extract the entries since the last sync from Debian. - ''' + """ changelog = Changelog(srcpkg.getChangelog()) if changelog is None: - return '' + return "" delta = [] debian_info = DebianDistroInfo() for block in changelog: - distribution = block.distributions.split()[0].split('-')[0] + distribution = block.distributions.split()[0].split("-")[0] if debian_info.valid(distribution): break - delta += [str(change) for change in block.changes() - if change.strip()] + delta += [str(change) for change in block.changes() if change.strip()] - return '\n'.join(delta) + return "\n".join(delta) -def mail_bug(srcpkg, subscribe, status, bugtitle, bugtext, bug_mail_domain, - keyid, myemailaddr, mailserver_host, mailserver_port, - mailserver_user, mailserver_pass): - ''' +def mail_bug( + srcpkg, + subscribe, + status, + bugtitle, + bugtext, + bug_mail_domain, + keyid, + myemailaddr, + mailserver_host, + mailserver_port, + mailserver_user, + mailserver_pass, +): + """ Submit the sync request per email. - ''' + """ - to = 'new@' + bug_mail_domain + to = "new@" + bug_mail_domain # generate mailbody if srcpkg: - mailbody = ' affects ubuntu/%s\n' % srcpkg + mailbody = " affects ubuntu/%s\n" % srcpkg else: - mailbody = ' affects ubuntu\n' - mailbody += '''\ + mailbody = " affects ubuntu\n" + mailbody += """\ status %s importance wishlist subscribe %s done -%s''' % (status, subscribe, bugtext) +%s""" % ( + status, + subscribe, + bugtext, + ) # prepare sign command gpg_command = None - for cmd in ('gnome-gpg', 'gpg2', 'gpg'): - if os.access('/usr/bin/%s' % cmd, os.X_OK): + for cmd in ("gnome-gpg", "gpg2", "gpg"): + if os.access("/usr/bin/%s" % cmd, os.X_OK): gpg_command = [cmd] break @@ -141,107 +160,135 @@ def mail_bug(srcpkg, subscribe, status, bugtitle, bugtext, bug_mail_domain, Logger.error("Cannot locate gpg, please install the 'gnupg' package!") sys.exit(1) - gpg_command.append('--clearsign') + gpg_command.append("--clearsign") if keyid: - gpg_command.extend(('-u', keyid)) + gpg_command.extend(("-u", keyid)) # sign the mail body gpg = subprocess.Popen( - gpg_command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, - encoding='utf-8') + gpg_command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, encoding="utf-8" + ) signed_report = gpg.communicate(mailbody)[0] if gpg.returncode != 0: Logger.error("%s failed.", gpg_command[0]) sys.exit(1) # generate email - mail = '''\ + mail = """\ From: %s To: %s Subject: %s Content-Type: text/plain; charset=UTF-8 -%s''' % (myemailaddr, to, bugtitle, signed_report) +%s""" % ( + myemailaddr, + to, + bugtitle, + signed_report, + ) - print('The final report is:\n%s' % mail) + print("The final report is:\n%s" % mail) confirmation_prompt() # save mail in temporary file backup = tempfile.NamedTemporaryFile( - mode='w', + mode="w", delete=False, - prefix='requestsync-' + re.sub(r'[^a-zA-Z0-9_-]', '', bugtitle.replace(' ', '_')) + prefix="requestsync-" + re.sub(r"[^a-zA-Z0-9_-]", "", bugtitle.replace(" ", "_")), ) with backup: backup.write(mail) - Logger.info('The e-mail has been saved in %s and will be deleted ' - 'after succesful transmission', backup.name) + Logger.info( + "The e-mail has been saved in %s and will be deleted after succesful transmission", + backup.name, + ) # connect to the server while True: try: - Logger.info('Connecting to %s:%s ...', mailserver_host, - mailserver_port) + Logger.info("Connecting to %s:%s ...", mailserver_host, mailserver_port) s = smtplib.SMTP(mailserver_host, mailserver_port) break except smtplib.SMTPConnectError as s: try: # py2 path # pylint: disable=unsubscriptable-object - Logger.error('Could not connect to %s:%s: %s (%i)', - mailserver_host, mailserver_port, s[1], s[0]) + Logger.error( + "Could not connect to %s:%s: %s (%i)", + mailserver_host, + mailserver_port, + s[1], + s[0], + ) except TypeError: # pylint: disable=no-member - Logger.error('Could not connect to %s:%s: %s (%i)', - mailserver_host, mailserver_port, s.strerror, s.errno) + Logger.error( + "Could not connect to %s:%s: %s (%i)", + mailserver_host, + mailserver_port, + s.strerror, + s.errno, + ) if s.smtp_code == 421: - confirmation_prompt(message='This is a temporary error, press [Enter] ' - 'to retry. Press [Ctrl-C] to abort now.') + confirmation_prompt( + message="This is a temporary error, press [Enter] " + "to retry. Press [Ctrl-C] to abort now." + ) except socket.error as s: try: # py2 path # pylint: disable=unsubscriptable-object - Logger.error('Could not connect to %s:%s: %s (%i)', - mailserver_host, mailserver_port, s[1], s[0]) + Logger.error( + "Could not connect to %s:%s: %s (%i)", + mailserver_host, + mailserver_port, + s[1], + s[0], + ) except TypeError: # pylint: disable=no-member - Logger.error('Could not connect to %s:%s: %s (%i)', - mailserver_host, mailserver_port, s.strerror, s.errno) + Logger.error( + "Could not connect to %s:%s: %s (%i)", + mailserver_host, + mailserver_port, + s.strerror, + s.errno, + ) return if mailserver_user and mailserver_pass: try: s.login(mailserver_user, mailserver_pass) except smtplib.SMTPAuthenticationError: - Logger.error('Error authenticating to the server: ' - 'invalid username and password.') + Logger.error("Error authenticating to the server: invalid username and password.") s.quit() return except smtplib.SMTPException: - Logger.error('Unknown SMTP error.') + Logger.error("Unknown SMTP error.") s.quit() return while True: try: - s.sendmail(myemailaddr, to, mail.encode('utf-8')) + s.sendmail(myemailaddr, to, mail.encode("utf-8")) s.quit() os.remove(backup.name) - Logger.info('Sync request mailed.') + Logger.info("Sync request mailed.") break except smtplib.SMTPRecipientsRefused as smtperror: smtp_code, smtp_message = smtperror.recipients[to] - Logger.error('Error while sending: %i, %s', smtp_code, smtp_message) + Logger.error("Error while sending: %i, %s", smtp_code, smtp_message) if smtp_code == 450: - confirmation_prompt(message='This is a temporary error, press [Enter] ' - 'to retry. Press [Ctrl-C] to abort now.') + confirmation_prompt( + message="This is a temporary error, press [Enter] " + "to retry. Press [Ctrl-C] to abort now." + ) else: return except smtplib.SMTPResponseException as e: - Logger.error('Error while sending: %i, %s', - e.smtp_code, e.smtp_error) + Logger.error("Error while sending: %i, %s", e.smtp_code, e.smtp_error) return except smtplib.SMTPServerDisconnected: - Logger.error('Server disconnected while sending the mail.') + Logger.error("Server disconnected while sending the mail.") return diff --git a/ubuntutools/sponsor_patch/bugtask.py b/ubuntutools/sponsor_patch/bugtask.py index 7c75037..f91020a 100644 --- a/ubuntutools/sponsor_patch/bugtask.py +++ b/ubuntutools/sponsor_patch/bugtask.py @@ -26,6 +26,7 @@ import httplib2 from ubuntutools.version import Version import logging + Logger = logging.getLogger(__name__) @@ -58,7 +59,7 @@ class BugTask(object): self.series = components[2].lower() if self.package is None: - title_re = r'^Sync ([a-z0-9+.-]+) [a-z0-9.+:~-]+ \([a-z]+\) from.*' + title_re = r"^Sync ([a-z0-9+.-]+) [a-z0-9.+:~-]+ \([a-z]+\) from.*" match = re.match(title_re, self.get_bug_title(), re.U | re.I) if match is not None: self.package = match.group(1) @@ -74,7 +75,7 @@ class BugTask(object): if url.endswith(".dsc"): response, data = httplib2.Http().request(url) assert response.status == 200 - with open(filename, 'wb') as f: + with open(filename, "wb") as f: f.write(data) dsc_file = os.path.join(os.getcwd(), filename) @@ -84,18 +85,26 @@ class BugTask(object): return dsc_file def get_branch_link(self): - return "lp:" + self.project + "/" + self.get_series() + "/" + \ - self.package + return "lp:" + self.project + "/" + self.get_series() + "/" + self.package def get_bug_title(self): """Returns the title of the related bug.""" return self.bug_task.bug.title def get_long_info(self): - return "Bug task: " + str(self.bug_task) + "\n" + \ - "Package: " + str(self.package) + "\n" + \ - "Project: " + str(self.project) + "\n" + \ - "Series: " + str(self.series) + return ( + "Bug task: " + + str(self.bug_task) + + "\n" + + "Package: " + + str(self.package) + + "\n" + + "Project: " + + str(self.project) + + "\n" + + "Series: " + + str(self.series) + ) def get_lp_task(self): """Returns the Launchpad bug task object.""" @@ -137,14 +146,16 @@ class BugTask(object): dist = self.launchpad.distributions[project] archive = dist.getArchive(name="primary") distro_series = dist.getSeries(name_or_version=series) - published = archive.getPublishedSources(source_name=self.package, - distro_series=distro_series, - status="Published", - exact_match=True) + published = archive.getPublishedSources( + source_name=self.package, + distro_series=distro_series, + status="Published", + exact_match=True, + ) latest_source = None for source in published: - if source.pocket in ('Release', 'Security', 'Updates', 'Proposed'): + if source.pocket in ("Release", "Security", "Updates", "Proposed"): latest_source = source break return latest_source @@ -156,7 +167,7 @@ class BugTask(object): def get_latest_released_version(self): source = self.get_source(True) if source is None: # Not currently published in Ubuntu - version = '~' + version = "~" else: version = source.source_package_version return Version(version) diff --git a/ubuntutools/sponsor_patch/patch.py b/ubuntutools/sponsor_patch/patch.py index 7e12242..d6aa8de 100644 --- a/ubuntutools/sponsor_patch/patch.py +++ b/ubuntutools/sponsor_patch/patch.py @@ -23,6 +23,7 @@ from ubuntutools.sponsor_patch.question import ask_for_manual_fixing from functools import reduce import logging + Logger = logging.getLogger(__name__) @@ -32,10 +33,10 @@ class Patch(object): def __init__(self, patch): self._patch = patch self._patch_file = re.sub(" |/", "_", patch.title) - if not reduce(lambda r, x: r or self._patch.title.endswith(x), - (".debdiff", ".diff", ".patch"), False): - Logger.debug("Patch %s does not have a proper file extension." % - (self._patch.title)) + if not reduce( + lambda r, x: r or self._patch.title.endswith(x), (".debdiff", ".diff", ".patch"), False + ): + Logger.debug("Patch %s does not have a proper file extension." % (self._patch.title)) self._patch_file += ".patch" self._full_path = os.path.realpath(self._patch_file) self._changed_files = None @@ -45,21 +46,36 @@ class Patch(object): assert self._changed_files is not None, "You forgot to download the patch." edit = False if self.is_debdiff(): - cmd = ["patch", "--merge", "--force", "-p", - str(self.get_strip_level()), "-i", self._full_path] - Logger.debug(' '.join(cmd)) + cmd = [ + "patch", + "--merge", + "--force", + "-p", + str(self.get_strip_level()), + "-i", + self._full_path, + ] + Logger.debug(" ".join(cmd)) if subprocess.call(cmd) != 0: - Logger.error("Failed to apply debdiff %s to %s %s.", - self._patch_file, task.package, task.get_version()) + Logger.error( + "Failed to apply debdiff %s to %s %s.", + self._patch_file, + task.package, + task.get_version(), + ) if not edit: ask_for_manual_fixing() edit = True else: cmd = ["add-patch", self._full_path] - Logger.debug(' '.join(cmd)) + Logger.debug(" ".join(cmd)) if subprocess.call(cmd) != 0: - Logger.error("Failed to apply diff %s to %s %s.", - self._patch_file, task.package, task.get_version()) + Logger.error( + "Failed to apply diff %s to %s %s.", + self._patch_file, + task.package, + task.get_version(), + ) if not edit: ask_for_manual_fixing() edit = True @@ -73,7 +89,7 @@ class Patch(object): patch_f.close() cmd = ["diffstat", "-l", "-p0", self._full_path] - changed_files = subprocess.check_output(cmd, encoding='utf-8') + changed_files = subprocess.check_output(cmd, encoding="utf-8") self._changed_files = [f for f in changed_files.split("\n") if f != ""] def get_strip_level(self): @@ -81,13 +97,11 @@ class Patch(object): assert self._changed_files is not None, "You forgot to download the patch." strip_level = None if self.is_debdiff(): - changelog = [f for f in self._changed_files - if f.endswith("debian/changelog")][0] + changelog = [f for f in self._changed_files if f.endswith("debian/changelog")][0] strip_level = len(changelog.split(os.sep)) - 2 return strip_level def is_debdiff(self): """Checks if the patch is a debdiff (= modifies debian/changelog).""" assert self._changed_files is not None, "You forgot to download the patch." - return len([f for f in self._changed_files - if f.endswith("debian/changelog")]) > 0 + return len([f for f in self._changed_files if f.endswith("debian/changelog")]) > 0 diff --git a/ubuntutools/sponsor_patch/question.py b/ubuntutools/sponsor_patch/question.py index b49ff99..56afc9d 100644 --- a/ubuntutools/sponsor_patch/question.py +++ b/ubuntutools/sponsor_patch/question.py @@ -37,8 +37,7 @@ def ask_for_ignoring_or_fixing(): def ask_for_manual_fixing(): """Ask the user to resolve an issue manually.""" - answer = YesNoQuestion().ask("Do you want to resolve this issue manually", - "yes") + answer = YesNoQuestion().ask("Do you want to resolve this issue manually", "yes") if answer == "no": user_abort() diff --git a/ubuntutools/sponsor_patch/source_package.py b/ubuntutools/sponsor_patch/source_package.py index f21177c..bad9ac8 100644 --- a/ubuntutools/sponsor_patch/source_package.py +++ b/ubuntutools/sponsor_patch/source_package.py @@ -25,20 +25,24 @@ import debian.deb822 from ubuntutools.question import Question, YesNoQuestion -from ubuntutools.sponsor_patch.question import (ask_for_ignoring_or_fixing, - ask_for_manual_fixing, - user_abort) +from ubuntutools.sponsor_patch.question import ( + ask_for_ignoring_or_fixing, + ask_for_manual_fixing, + user_abort, +) import logging + Logger = logging.getLogger(__name__) def _get_series(launchpad): """Returns a tuple with the development and list of supported series.""" - ubuntu = launchpad.distributions['ubuntu'] + ubuntu = launchpad.distributions["ubuntu"] devel_series = ubuntu.current_series.name - supported_series = [series.name for series in ubuntu.series - if series.active and series.name != devel_series] + supported_series = [ + series.name for series in ubuntu.series if series.active and series.name != devel_series + ] return (devel_series, supported_series) @@ -49,10 +53,10 @@ def strip_epoch(version): return "1.1.3-1". """ - parts = version.full_version.split(':') + parts = version.full_version.split(":") if len(parts) > 1: del parts[0] - version_without_epoch = ':'.join(parts) + version_without_epoch = ":".join(parts) return version_without_epoch @@ -74,8 +78,7 @@ class SourcePackage(object): if upload == "ubuntu": self._print_logs() question = Question(["yes", "edit", "no"]) - answer = question.ask("Do you want to acknowledge the sync request", - "no") + answer = question.ask("Do you want to acknowledge the sync request", "no") if answer == "edit": return False elif answer == "no": @@ -90,33 +93,33 @@ class SourcePackage(object): msg = "Sync request ACK'd." if self._build_log: - msg = ("%s %s builds on %s. " + msg) % \ - (self._package, self._version, - self._builder.get_architecture()) + msg = ("%s %s builds on %s. " + msg) % ( + self._package, + self._version, + self._builder.get_architecture(), + ) bug.newMessage(content=msg, subject="sponsor-patch") Logger.debug("Acknowledged sync request bug #%i.", bug.id) - bug.subscribe(person=launchpad.people['ubuntu-archive']) + bug.subscribe(person=launchpad.people["ubuntu-archive"]) Logger.debug("Subscribed ubuntu-archive to bug #%i.", bug.id) bug.subscribe(person=launchpad.me) Logger.debug("Subscribed me to bug #%i.", bug.id) - sponsorsteam = launchpad.people['ubuntu-sponsors'] + sponsorsteam = launchpad.people["ubuntu-sponsors"] for sub in bug.subscriptions: if sub.person == sponsorsteam and sub.canBeUnsubscribedByUser(): - bug.unsubscribe(person=launchpad.people['ubuntu-sponsors']) - Logger.debug("Unsubscribed ubuntu-sponsors from bug #%i.", - bug.id) + bug.unsubscribe(person=launchpad.people["ubuntu-sponsors"]) + Logger.debug("Unsubscribed ubuntu-sponsors from bug #%i.", bug.id) elif sub.person == sponsorsteam: - Logger.debug("Couldn't unsubscribe ubuntu-sponsors from " - "bug #%i.", bug.id) + Logger.debug("Couldn't unsubscribe ubuntu-sponsors from bug #%i.", bug.id) - Logger.info("Successfully acknowledged sync request bug #%i.", - bug.id) + Logger.info("Successfully acknowledged sync request bug #%i.", bug.id) else: - Logger.error("Sync requests can only be acknowledged when the " - "upload target is Ubuntu.") + Logger.error( + "Sync requests can only be acknowledged when the upload target is Ubuntu." + ) sys.exit(1) return True @@ -141,28 +144,29 @@ class SourcePackage(object): elif answer == "no": user_abort() cmd = ["dput", "--force", upload, self._changes_file] - Logger.debug(' '.join(cmd)) + Logger.debug(" ".join(cmd)) if subprocess.call(cmd) != 0: - Logger.error("Upload of %s to %s failed." % - (os.path.basename(self._changes_file), upload)) + Logger.error( + "Upload of %s to %s failed." % (os.path.basename(self._changes_file), upload) + ) sys.exit(1) # Push the branch if the package is uploaded to the Ubuntu archive. if upload == "ubuntu" and self._branch: - cmd = ['debcommit'] - Logger.debug(' '.join(cmd)) + cmd = ["debcommit"] + Logger.debug(" ".join(cmd)) if subprocess.call(cmd) != 0: - Logger.error('Bzr commit failed.') + Logger.error("Bzr commit failed.") sys.exit(1) - cmd = ['bzr', 'mark-uploaded'] - Logger.debug(' '.join(cmd)) + cmd = ["bzr", "mark-uploaded"] + Logger.debug(" ".join(cmd)) if subprocess.call(cmd) != 0: - Logger.error('Bzr tagging failed.') + Logger.error("Bzr tagging failed.") sys.exit(1) - cmd = ['bzr', 'push', ':parent'] - Logger.debug(' '.join(cmd)) + cmd = ["bzr", "push", ":parent"] + Logger.debug(" ".join(cmd)) if subprocess.call(cmd) != 0: - Logger.error('Bzr push failed.') + Logger.error("Bzr push failed.") sys.exit(1) return True @@ -175,8 +179,9 @@ class SourcePackage(object): if dist is None: dist = re.sub("-.*$", "", self._changelog.distributions) - build_name = "{}_{}_{}.build".format(self._package, strip_epoch(self._version), - self._builder.get_architecture()) + build_name = "{}_{}_{}.build".format( + self._package, strip_epoch(self._version), self._builder.get_architecture() + ) self._build_log = os.path.join(self._buildresult, build_name) successful_built = False @@ -191,8 +196,7 @@ class SourcePackage(object): update = False # build package - result = self._builder.build(self._dsc_file, dist, - self._buildresult) + result = self._builder.build(self._dsc_file, dist, self._buildresult) if result != 0: question = Question(["yes", "update", "retry", "no"]) answer = question.ask("Do you want to resolve this issue manually", "yes") @@ -224,13 +228,14 @@ class SourcePackage(object): """ if self._branch: - cmd = ['bzr', 'builddeb', '--builder=debuild', '-S', - '--', '--no-lintian', '-nc'] + cmd = ["bzr", "builddeb", "--builder=debuild", "-S", "--", "--no-lintian", "-nc"] else: - cmd = ['debuild', '--no-lintian', '-nc', '-S'] + cmd = ["debuild", "--no-lintian", "-nc", "-S"] cmd.append("-v" + previous_version.full_version) - if previous_version.upstream_version == \ - self._changelog.upstream_version and upload == "ubuntu": + if ( + previous_version.upstream_version == self._changelog.upstream_version + and upload == "ubuntu" + ): # FIXME: Add proper check that catches cases like changed # compression (.tar.gz -> tar.bz2) and multiple orig source tarballs cmd.append("-sd") @@ -239,9 +244,9 @@ class SourcePackage(object): if keyid is not None: cmd += ["-k" + keyid] env = os.environ - if upload == 'ubuntu': - env['DEB_VENDOR'] = 'Ubuntu' - Logger.debug(' '.join(cmd)) + if upload == "ubuntu": + env["DEB_VENDOR"] = "Ubuntu" + Logger.debug(" ".join(cmd)) if subprocess.call(cmd, env=env) != 0: Logger.error("Failed to build source tarball.") # TODO: Add a "retry" option @@ -252,8 +257,9 @@ class SourcePackage(object): @property def _changes_file(self): """Returns the file name of the .changes file.""" - return os.path.join(self._workdir, "{}_{}_source.changes" - .format(self._package, strip_epoch(self._version))) + return os.path.join( + self._workdir, "{}_{}_source.changes".format(self._package, strip_epoch(self._version)) + ) def check_target(self, upload, launchpad): """Make sure that the target is correct. @@ -265,18 +271,22 @@ class SourcePackage(object): (devel_series, supported_series) = _get_series(launchpad) if upload == "ubuntu": - allowed = supported_series + \ - [s + "-proposed" for s in supported_series] + \ - [devel_series] + allowed = ( + supported_series + [s + "-proposed" for s in supported_series] + [devel_series] + ) if self._changelog.distributions not in allowed: - Logger.error("%s is not an allowed series. It needs to be one of %s." % - (self._changelog.distributions, ", ".join(allowed))) + Logger.error( + "%s is not an allowed series. It needs to be one of %s." + % (self._changelog.distributions, ", ".join(allowed)) + ) return ask_for_ignoring_or_fixing() elif upload and upload.startswith("ppa/"): allowed = supported_series + [devel_series] if self._changelog.distributions not in allowed: - Logger.error("%s is not an allowed series. It needs to be one of %s." % - (self._changelog.distributions, ", ".join(allowed))) + Logger.error( + "%s is not an allowed series. It needs to be one of %s." + % (self._changelog.distributions, ", ".join(allowed)) + ) return ask_for_ignoring_or_fixing() return True @@ -288,14 +298,17 @@ class SourcePackage(object): """ if self._version <= previous_version: - Logger.error("The version %s is not greater than the already " - "available %s.", self._version, previous_version) + Logger.error( + "The version %s is not greater than the already available %s.", + self._version, + previous_version, + ) return ask_for_ignoring_or_fixing() return True def check_sync_request_version(self, bug_number, task): """Check if the downloaded version of the package is mentioned in the - bug title.""" + bug title.""" if not task.title_contains(self._version): print("Bug #%i title: %s" % (bug_number, task.get_bug_title())) @@ -313,20 +326,20 @@ class SourcePackage(object): @property def _dsc_file(self): """Returns the file name of the .dsc file.""" - return os.path.join(self._workdir, "{}_{}.dsc".format(self._package, - strip_epoch(self._version))) + return os.path.join( + self._workdir, "{}_{}.dsc".format(self._package, strip_epoch(self._version)) + ) def generate_debdiff(self, dsc_file): """Generates a debdiff between the given .dsc file and this source - package.""" + package.""" assert os.path.isfile(dsc_file), "%s does not exist." % (dsc_file) - assert os.path.isfile(self._dsc_file), "%s does not exist." % \ - (self._dsc_file) + assert os.path.isfile(self._dsc_file), "%s does not exist." % (self._dsc_file) cmd = ["debdiff", dsc_file, self._dsc_file] if not Logger.isEnabledFor(logging.DEBUG): cmd.insert(1, "-q") - Logger.debug(' '.join(cmd) + " > " + self._debdiff_filename) + Logger.debug(" ".join(cmd) + " > " + self._debdiff_filename) with open(self._debdiff_filename, "w") as debdiff_file: debdiff = subprocess.run(cmd, check=False, stdout=debdiff_file) assert debdiff.returncode in (0, 1) @@ -376,8 +389,7 @@ class SourcePackage(object): # Check the changelog self._changelog = debian.changelog.Changelog() try: - self._changelog.parse_changelog(open("debian/changelog"), - max_blocks=1, strict=True) + self._changelog.parse_changelog(open("debian/changelog"), max_blocks=1, strict=True) except debian.changelog.ChangelogParseError as error: Logger.error("The changelog entry doesn't validate: %s", str(error)) ask_for_manual_fixing() @@ -387,8 +399,10 @@ class SourcePackage(object): try: self._version = self._changelog.get_version() except IndexError: - Logger.error("Debian package version could not be determined. " - "debian/changelog is probably malformed.") + Logger.error( + "Debian package version could not be determined. " + "debian/changelog is probably malformed." + ) ask_for_manual_fixing() return False @@ -402,22 +416,26 @@ class SourcePackage(object): # Determine whether to use the source or binary build for lintian if self._build_log: - build_changes = self._package + "_" + strip_epoch(self._version) + \ - "_" + self._builder.get_architecture() + ".changes" + build_changes = ( + self._package + + "_" + + strip_epoch(self._version) + + "_" + + self._builder.get_architecture() + + ".changes" + ) changes_for_lintian = os.path.join(self._buildresult, build_changes) else: changes_for_lintian = self._changes_file # Check lintian - assert os.path.isfile(changes_for_lintian), "%s does not exist." % \ - (changes_for_lintian) - cmd = ["lintian", "-IE", "--pedantic", "-q", "--profile", "ubuntu", - changes_for_lintian] - lintian_filename = os.path.join(self._workdir, - self._package + "_" + - strip_epoch(self._version) + ".lintian") - Logger.debug(' '.join(cmd) + " > " + lintian_filename) - report = subprocess.check_output(cmd, encoding='utf-8') + assert os.path.isfile(changes_for_lintian), "%s does not exist." % (changes_for_lintian) + cmd = ["lintian", "-IE", "--pedantic", "-q", "--profile", "ubuntu", changes_for_lintian] + lintian_filename = os.path.join( + self._workdir, self._package + "_" + strip_epoch(self._version) + ".lintian" + ) + Logger.debug(" ".join(cmd) + " > " + lintian_filename) + report = subprocess.check_output(cmd, encoding="utf-8") # write lintian report file lintian_file = open(lintian_filename, "w") @@ -430,17 +448,25 @@ class SourcePackage(object): """Does a sync of the source package.""" if upload == "ubuntu": - cmd = ["syncpackage", self._package, "-b", str(bug_number), "-f", - "-s", requester, "-V", str(self._version), - "-d", series] - Logger.debug(' '.join(cmd)) + cmd = [ + "syncpackage", + self._package, + "-b", + str(bug_number), + "-f", + "-s", + requester, + "-V", + str(self._version), + "-d", + series, + ] + Logger.debug(" ".join(cmd)) if subprocess.call(cmd) != 0: - Logger.error("Syncing of %s %s failed.", self._package, - str(self._version)) + Logger.error("Syncing of %s %s failed.", self._package, str(self._version)) sys.exit(1) else: # FIXME: Support this use case! - Logger.error("Uploading a synced package other than to Ubuntu " - "is not supported yet!") + Logger.error("Uploading a synced package other than to Ubuntu is not supported yet!") sys.exit(1) return True diff --git a/ubuntutools/sponsor_patch/sponsor_patch.py b/ubuntutools/sponsor_patch/sponsor_patch.py index bd9baa9..9f721d8 100644 --- a/ubuntutools/sponsor_patch/sponsor_patch.py +++ b/ubuntutools/sponsor_patch/sponsor_patch.py @@ -25,8 +25,7 @@ from distro_info import UbuntuDistroInfo from launchpadlib.launchpad import Launchpad -from ubuntutools.update_maintainer import (update_maintainer, - MaintainerUpdateException) +from ubuntutools.update_maintainer import update_maintainer, MaintainerUpdateException from ubuntutools.question import input_number from ubuntutools.sponsor_patch.bugtask import BugTask, is_sync @@ -35,34 +34,35 @@ from ubuntutools.sponsor_patch.question import ask_for_manual_fixing from ubuntutools.sponsor_patch.source_package import SourcePackage import logging + Logger = logging.getLogger(__name__) def is_command_available(command, check_sbin=False): "Is command in $PATH?" - path = os.environ.get('PATH', '/usr/bin:/bin').split(':') + path = os.environ.get("PATH", "/usr/bin:/bin").split(":") if check_sbin: - path += [directory[:-3] + 'sbin' - for directory in path if directory.endswith('/bin')] - return any(os.access(os.path.join(directory, command), os.X_OK) - for directory in path) + path += [directory[:-3] + "sbin" for directory in path if directory.endswith("/bin")] + return any(os.access(os.path.join(directory, command), os.X_OK) for directory in path) def check_dependencies(): "Do we have all the commands we need for full functionality?" missing = [] - for cmd in ('patch', 'bzr', 'quilt', 'dput', 'lintian'): + for cmd in ("patch", "bzr", "quilt", "dput", "lintian"): if not is_command_available(cmd): missing.append(cmd) - if not is_command_available('bzr-buildpackage'): - missing.append('bzr-builddeb') - if not any(is_command_available(cmd, check_sbin=True) - for cmd in ('pbuilder', 'sbuild', 'cowbuilder')): - missing.append('pbuilder/cowbuilder/sbuild') + if not is_command_available("bzr-buildpackage"): + missing.append("bzr-builddeb") + if not any( + is_command_available(cmd, check_sbin=True) for cmd in ("pbuilder", "sbuild", "cowbuilder") + ): + missing.append("pbuilder/cowbuilder/sbuild") if missing: - Logger.warning("sponsor-patch requires %s to be installed for full " - "functionality", ', '.join(missing)) + Logger.warning( + "sponsor-patch requires %s to be installed for full functionality", ", ".join(missing) + ) def get_source_package_name(bug_task): @@ -84,12 +84,16 @@ def get_user_shell(): def edit_source(): # Spawn shell to allow modifications cmd = [get_user_shell()] - Logger.debug(' '.join(cmd)) - print("""An interactive shell was launched in + Logger.debug(" ".join(cmd)) + print( + """An interactive shell was launched in file://%s Edit your files. When you are done, exit the shell. If you wish to abort the process, exit the shell such that it returns an exit code other than zero. -""" % (os.getcwd()), end=' ') +""" + % (os.getcwd()), + end=" ", + ) returncode = subprocess.call(cmd) if returncode != 0: Logger.error("Shell exited with exit value %i." % (returncode)) @@ -100,11 +104,15 @@ def ask_for_patch_or_branch(bug, attached_patches, linked_branches): patch = None branch = None if len(attached_patches) == 0: - msg = "https://launchpad.net/bugs/%i has %i branches linked:" % \ - (bug.id, len(linked_branches)) + msg = "https://launchpad.net/bugs/%i has %i branches linked:" % ( + bug.id, + len(linked_branches), + ) elif len(linked_branches) == 0: - msg = "https://launchpad.net/bugs/%i has %i patches attached:" % \ - (bug.id, len(attached_patches)) + msg = "https://launchpad.net/bugs/%i has %i patches attached:" % ( + bug.id, + len(attached_patches), + ) else: branches = "%i branch" % len(linked_branches) if len(linked_branches) > 1: @@ -112,8 +120,11 @@ def ask_for_patch_or_branch(bug, attached_patches, linked_branches): patches = "%i patch" % len(attached_patches) if len(attached_patches) > 1: patches += "es" - msg = "https://launchpad.net/bugs/%i has %s linked and %s attached:" % \ - (bug.id, branches, patches) + msg = "https://launchpad.net/bugs/%i has %s linked and %s attached:" % ( + bug.id, + branches, + patches, + ) Logger.info(msg) i = 0 for linked_branch in linked_branches: @@ -122,8 +133,7 @@ def ask_for_patch_or_branch(bug, attached_patches, linked_branches): for attached_patch in attached_patches: i += 1 print("%i) %s" % (i, attached_patch.title)) - selected = input_number("Which branch or patch do you want to download", - 1, i, i) + selected = input_number("Which branch or patch do you want to download", 1, i, i) if selected <= len(linked_branches): branch = linked_branches[selected - 1].bzr_identity else: @@ -139,21 +149,26 @@ def get_patch_or_branch(bug): linked_branches = [b.branch for b in bug.linked_branches] if len(attached_patches) == 0 and len(linked_branches) == 0: if len(bug.attachments) == 0: - Logger.error("No attachment and no linked branch found on " - "bug #%i. Add the tag sync to the bug if it is " - "a sync request.", bug.id) + Logger.error( + "No attachment and no linked branch found on " + "bug #%i. Add the tag sync to the bug if it is " + "a sync request.", + bug.id, + ) else: - Logger.error("No attached patch and no linked branch found. " - "Go to https://launchpad.net/bugs/%i and mark an " - "attachment as patch.", bug.id) + Logger.error( + "No attached patch and no linked branch found. " + "Go to https://launchpad.net/bugs/%i and mark an " + "attachment as patch.", + bug.id, + ) sys.exit(1) elif len(attached_patches) == 1 and len(linked_branches) == 0: patch = Patch(attached_patches[0]) elif len(attached_patches) == 0 and len(linked_branches) == 1: branch = linked_branches[0].bzr_identity else: - patch, branch = ask_for_patch_or_branch(bug, attached_patches, - linked_branches) + patch, branch = ask_for_patch_or_branch(bug, attached_patches, linked_branches) return (patch, branch) @@ -162,7 +177,7 @@ def download_branch(branch): if os.path.isdir(dir_name): shutil.rmtree(dir_name) cmd = ["bzr", "branch", branch] - Logger.debug(' '.join(cmd)) + Logger.debug(" ".join(cmd)) if subprocess.call(cmd) != 0: Logger.error("Failed to download branch %s." % (branch)) sys.exit(1) @@ -172,7 +187,7 @@ def download_branch(branch): def merge_branch(branch): edit = False cmd = ["bzr", "merge", branch] - Logger.debug(' '.join(cmd)) + Logger.debug(" ".join(cmd)) if subprocess.call(cmd) != 0: Logger.error("Failed to merge branch %s." % (branch)) ask_for_manual_fixing() @@ -184,7 +199,7 @@ def extract_source(dsc_file, verbose=False): cmd = ["dpkg-source", "--skip-patches", "-x", dsc_file] if not verbose: cmd.insert(1, "-q") - Logger.debug(' '.join(cmd)) + Logger.debug(" ".join(cmd)) if subprocess.call(cmd) != 0: Logger.error("Extraction of %s failed." % (os.path.basename(dsc_file))) sys.exit(1) @@ -201,7 +216,7 @@ def get_open_ubuntu_bug_task(launchpad, bug, branch=None): ubuntu_tasks = [x for x in bug_tasks if x.is_ubuntu_task()] bug_id = bug.id if branch: - branch = branch.split('/') + branch = branch.split("/") # Non-production LP? if len(branch) > 5: branch = branch[3:] @@ -211,9 +226,8 @@ def get_open_ubuntu_bug_task(launchpad, bug, branch=None): sys.exit(1) elif len(ubuntu_tasks) == 1: task = ubuntu_tasks[0] - if len(ubuntu_tasks) > 1 and branch and branch[1] == 'ubuntu': - tasks = [t for t in ubuntu_tasks if - t.get_series() == branch[2] and t.package == branch[3]] + if len(ubuntu_tasks) > 1 and branch and branch[1] == "ubuntu": + tasks = [t for t in ubuntu_tasks if t.get_series() == branch[2] and t.package == branch[3]] if len(tasks) > 1: # A bug targeted to the development series? tasks = [t for t in tasks if t.series is not None] @@ -221,19 +235,24 @@ def get_open_ubuntu_bug_task(launchpad, bug, branch=None): task = tasks[0] elif len(ubuntu_tasks) > 1: task_list = [t.get_short_info() for t in ubuntu_tasks] - Logger.debug("%i Ubuntu tasks exist for bug #%i.\n%s", len(ubuntu_tasks), - bug_id, "\n".join(task_list)) + Logger.debug( + "%i Ubuntu tasks exist for bug #%i.\n%s", + len(ubuntu_tasks), + bug_id, + "\n".join(task_list), + ) open_ubuntu_tasks = [x for x in ubuntu_tasks if not x.is_complete()] if len(open_ubuntu_tasks) == 1: task = open_ubuntu_tasks[0] else: - Logger.info("https://launchpad.net/bugs/%i has %i Ubuntu tasks:" % - (bug_id, len(ubuntu_tasks))) + Logger.info( + "https://launchpad.net/bugs/%i has %i Ubuntu tasks:" % (bug_id, len(ubuntu_tasks)) + ) for i in range(len(ubuntu_tasks)): - print("%i) %s" % (i + 1, - ubuntu_tasks[i].get_package_and_series())) - selected = input_number("To which Ubuntu task does the patch belong", - 1, len(ubuntu_tasks)) + print("%i) %s" % (i + 1, ubuntu_tasks[i].get_package_and_series())) + selected = input_number( + "To which Ubuntu task does the patch belong", 1, len(ubuntu_tasks) + ) task = ubuntu_tasks[selected - 1] Logger.debug("Selected Ubuntu task: %s" % (task.get_short_info())) return task @@ -246,8 +265,10 @@ def _create_and_change_into(workdir): try: os.makedirs(workdir) except os.error as error: - Logger.error("Failed to create the working directory %s [Errno %i]: %s." % - (workdir, error.errno, error.strerror)) + Logger.error( + "Failed to create the working directory %s [Errno %i]: %s." + % (workdir, error.errno, error.strerror) + ) sys.exit(1) if workdir != os.getcwd(): Logger.debug("cd " + workdir) @@ -267,7 +288,7 @@ def _update_maintainer_field(): def _update_timestamp(): """Run dch to update the timestamp of debian/changelog.""" cmd = ["dch", "--maintmaint", "--release", ""] - Logger.debug(' '.join(cmd)) + Logger.debug(" ".join(cmd)) if subprocess.call(cmd) != 0: Logger.debug("Failed to update timestamp in debian/changelog.") @@ -294,13 +315,12 @@ def _download_and_change_into(task, dsc_file, patch, branch): extract_source(dsc_file, Logger.isEnabledFor(logging.DEBUG)) # change directory - directory = task.package + '-' + task.get_version().upstream_version + directory = task.package + "-" + task.get_version().upstream_version Logger.debug("cd " + directory) os.chdir(directory) -def sponsor_patch(bug_number, build, builder, edit, keyid, lpinstance, update, - upload, workdir): +def sponsor_patch(bug_number, build, builder, edit, keyid, lpinstance, update, upload, workdir): workdir = os.path.realpath(os.path.expanduser(workdir)) _create_and_change_into(workdir) @@ -331,8 +351,7 @@ def sponsor_patch(bug_number, build, builder, edit, keyid, lpinstance, update, update = False else: # We are going to run lintian, so we need a source package - successful = source_package.build_source(None, upload, - previous_version) + successful = source_package.build_source(None, upload, previous_version) if successful: series = task.get_debian_source_series() @@ -363,8 +382,7 @@ def sponsor_patch(bug_number, build, builder, edit, keyid, lpinstance, update, _update_timestamp() - if not source_package.build_source(keyid, upload, - task.get_previous_version()): + if not source_package.build_source(keyid, upload, task.get_previous_version()): continue source_package.generate_debdiff(dsc_file) diff --git a/ubuntutools/test/example_package.py b/ubuntutools/test/example_package.py index d4be8cd..94ea686 100644 --- a/ubuntutools/test/example_package.py +++ b/ubuntutools/test/example_package.py @@ -23,38 +23,38 @@ from ubuntutools.version import Version class ExamplePackage(object): - def __init__(self, source='example', version='1.0-1', destdir='test-data'): + def __init__(self, source="example", version="1.0-1", destdir="test-data"): self.source = source self.version = Version(version) self.destdir = Path(destdir) self.env = dict(os.environ) - self.env['DEBFULLNAME'] = 'Example' - self.env['DEBEMAIL'] = 'example@example.net' + self.env["DEBFULLNAME"] = "Example" + self.env["DEBEMAIL"] = "example@example.net" @property def orig(self): - return self.destdir / f'{self.source}_{self.version.upstream_version}.orig.tar.xz' + return self.destdir / f"{self.source}_{self.version.upstream_version}.orig.tar.xz" @property def debian(self): - return self.destdir / f'{self.source}_{self.version}.debian.tar.xz' + return self.destdir / f"{self.source}_{self.version}.debian.tar.xz" @property def dsc(self): - return self.destdir / f'{self.source}_{self.version}.dsc' + return self.destdir / f"{self.source}_{self.version}.dsc" @property def dirname(self): - return f'{self.source}-{self.version.upstream_version}' + return f"{self.source}-{self.version.upstream_version}" @property def content_filename(self): - return 'content' + return "content" @property def content_text(self): - return 'my content' + return "my content" def create(self): with tempfile.TemporaryDirectory() as d: @@ -66,14 +66,24 @@ class ExamplePackage(object): (pkgdir / self.content_filename).write_text(self.content_text) # run dh_make to create orig tarball - subprocess.run('dh_make -sy --createorig'.split(), - check=True, env=self.env, cwd=str(pkgdir), - stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + subprocess.run( + "dh_make -sy --createorig".split(), + check=True, + env=self.env, + cwd=str(pkgdir), + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) # run dpkg-source -b to create debian tar and dsc - subprocess.run(f'dpkg-source -b {self.dirname}'.split(), - check=True, env=self.env, cwd=str(d), - stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + subprocess.run( + f"dpkg-source -b {self.dirname}".split(), + check=True, + env=self.env, + cwd=str(d), + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) # move tarballs and dsc to destdir self.destdir.mkdir(parents=True, exist_ok=True) diff --git a/ubuntutools/test/test_archive.py b/ubuntutools/test/test_archive.py index 71103b9..3912748 100644 --- a/ubuntutools/test/test_archive.py +++ b/ubuntutools/test/test_archive.py @@ -41,7 +41,7 @@ class DscVerificationTestCase(BaseVerificationTestCase): self.assertTrue(self.dsc.verify_file(self.pkg.debian)) def test_missing(self): - self.assertFalse(self.dsc.verify_file(self.pkg.destdir / 'does.not.exist')) + self.assertFalse(self.dsc.verify_file(self.pkg.destdir / "does.not.exist")) def test_bad(self): data = self.pkg.orig.read_bytes() @@ -51,13 +51,13 @@ class DscVerificationTestCase(BaseVerificationTestCase): self.assertFalse(self.dsc.verify_file(self.pkg.orig)) def test_sha1(self): - del self.dsc['Checksums-Sha256'] + del self.dsc["Checksums-Sha256"] self.test_good() self.test_bad() def test_md5(self): - del self.dsc['Checksums-Sha256'] - del self.dsc['Checksums-Sha1'] + del self.dsc["Checksums-Sha256"] + del self.dsc["Checksums-Sha1"] self.test_good() self.test_bad() @@ -72,7 +72,7 @@ class LocalSourcePackageTestCase(BaseVerificationTestCase): self.workdir = Path(d.name) def pull(self, **kwargs): - ''' Do the pull from pkg dir to the workdir, return the SourcePackage ''' + """Do the pull from pkg dir to the workdir, return the SourcePackage""" srcpkg = self.SourcePackage(dscfile=self.pkg.dsc, workdir=self.workdir, **kwargs) srcpkg.pull() return srcpkg @@ -85,11 +85,11 @@ class LocalSourcePackageTestCase(BaseVerificationTestCase): return srcpkg def test_unpack(self, **kwargs): - srcpkg = kwargs.get('srcpkg', self.pull(**kwargs)) + srcpkg = kwargs.get("srcpkg", self.pull(**kwargs)) srcpkg.unpack() content = self.workdir / self.pkg.dirname / self.pkg.content_filename self.assertEqual(self.pkg.content_text, content.read_text()) - debian = self.workdir / self.pkg.dirname / 'debian' + debian = self.workdir / self.pkg.dirname / "debian" self.assertTrue(debian.exists()) self.assertTrue(debian.is_dir()) @@ -103,12 +103,12 @@ class LocalSourcePackageTestCase(BaseVerificationTestCase): self.test_pull_and_unpack(package=self.pkg.source, version=self.pkg.version) def test_with_package_version_component(self): - self.test_pull_and_unpack(package=self.pkg.source, - version=self.pkg.version, - componet='main') + self.test_pull_and_unpack( + package=self.pkg.source, version=self.pkg.version, componet="main" + ) def test_verification(self): - corruption = b'CORRUPTION' + corruption = b"CORRUPTION" self.pull() diff --git a/ubuntutools/test/test_config.py b/ubuntutools/test/test_config.py index be5fc61..dcf2da5 100644 --- a/ubuntutools/test/test_config.py +++ b/ubuntutools/test/test_config.py @@ -17,7 +17,6 @@ import locale import os -# import sys import unittest from io import StringIO @@ -27,17 +26,14 @@ from ubuntutools.config import UDTConfig, ubu_email class ConfigTestCase(unittest.TestCase): - _config_files = { - 'system': '', - 'user': '', - } + _config_files = {"system": "", "user": ""} - def _fake_open(self, filename, mode='r'): - if mode != 'r': + def _fake_open(self, filename, mode="r"): + if mode != "r": raise IOError("Read only fake-file") files = { - '/etc/devscripts.conf': self._config_files['system'], - os.path.expanduser('~/.devscripts'): self._config_files['user'], + "/etc/devscripts.conf": self._config_files["system"], + os.path.expanduser("~/.devscripts"): self._config_files["user"], } if filename not in files: raise IOError("No such file or directory: '%s'" % filename) @@ -47,7 +43,7 @@ class ConfigTestCase(unittest.TestCase): super(ConfigTestCase, self).setUp() m = mock.mock_open() m.side_effect = self._fake_open - patcher = mock.patch('builtins.open', m) + patcher = mock.patch("builtins.open", m) self.addCleanup(patcher.stop) patcher.start() @@ -65,14 +61,16 @@ class ConfigTestCase(unittest.TestCase): self.clean_environment() def clean_environment(self): - self._config_files['system'] = '' - self._config_files['user'] = '' + self._config_files["system"] = "" + self._config_files["user"] = "" for k in list(os.environ.keys()): - if k.startswith(('UBUNTUTOOLS_', 'TEST_')): + if k.startswith(("UBUNTUTOOLS_", "TEST_")): del os.environ[k] def test_config_parsing(self): - self._config_files['user'] = """#COMMENT=yes + self._config_files[ + "user" + ] = """#COMMENT=yes \tTAB_INDENTED=yes SPACE_INDENTED=yes SPACE_SUFFIX=yes @@ -85,19 +83,22 @@ INHERIT=user REPEAT=no REPEAT=yes """ - self._config_files['system'] = 'INHERIT=system' - self.assertEqual(UDTConfig(prefix='TEST').config, { - 'TAB_INDENTED': 'yes', - 'SPACE_INDENTED': 'yes', - 'SPACE_SUFFIX': 'yes', - 'SINGLE_QUOTE': 'yes no', - 'DOUBLE_QUOTE': 'yes no', - 'QUOTED_QUOTE': "it's", - 'PAIR_QUOTES': 'yes a no', - 'COMMAND_EXECUTION': 'a', - 'INHERIT': 'user', - 'REPEAT': 'yes', - }) + self._config_files["system"] = "INHERIT=system" + self.assertEqual( + UDTConfig(prefix="TEST").config, + { + "TAB_INDENTED": "yes", + "SPACE_INDENTED": "yes", + "SPACE_SUFFIX": "yes", + "SINGLE_QUOTE": "yes no", + "DOUBLE_QUOTE": "yes no", + "QUOTED_QUOTE": "it's", + "PAIR_QUOTES": "yes a no", + "COMMAND_EXECUTION": "a", + "INHERIT": "user", + "REPEAT": "yes", + }, + ) # errs = Logger.stderr.getvalue().strip() # Logger.stderr = StringIO() # self.assertEqual(len(errs.splitlines()), 1) @@ -105,39 +106,40 @@ REPEAT=yes # r'Warning: Cannot parse.*\bCOMMAND_EXECUTION=a') def get_value(self, *args, **kwargs): - config = UDTConfig(prefix='TEST') + config = UDTConfig(prefix="TEST") return config.get_value(*args, **kwargs) def test_defaults(self): - self.assertEqual(self.get_value('BUILDER'), 'pbuilder') + self.assertEqual(self.get_value("BUILDER"), "pbuilder") def test_provided_default(self): - self.assertEqual(self.get_value('BUILDER', default='foo'), 'foo') + self.assertEqual(self.get_value("BUILDER", default="foo"), "foo") def test_scriptname_precedence(self): - self._config_files['user'] = """TEST_BUILDER=foo + self._config_files[ + "user" + ] = """TEST_BUILDER=foo UBUNTUTOOLS_BUILDER=bar""" - self.assertEqual(self.get_value('BUILDER'), 'foo') + self.assertEqual(self.get_value("BUILDER"), "foo") def test_configfile_precedence(self): - self._config_files['system'] = "UBUNTUTOOLS_BUILDER=foo" - self._config_files['user'] = "UBUNTUTOOLS_BUILDER=bar" - self.assertEqual(self.get_value('BUILDER'), 'bar') + self._config_files["system"] = "UBUNTUTOOLS_BUILDER=foo" + self._config_files["user"] = "UBUNTUTOOLS_BUILDER=bar" + self.assertEqual(self.get_value("BUILDER"), "bar") def test_environment_precedence(self): - self._config_files['user'] = "UBUNTUTOOLS_BUILDER=bar" - os.environ['UBUNTUTOOLS_BUILDER'] = 'baz' - self.assertEqual(self.get_value('BUILDER'), 'baz') + self._config_files["user"] = "UBUNTUTOOLS_BUILDER=bar" + os.environ["UBUNTUTOOLS_BUILDER"] = "baz" + self.assertEqual(self.get_value("BUILDER"), "baz") def test_general_environment_specific_config_precedence(self): - self._config_files['user'] = "TEST_BUILDER=bar" - os.environ['UBUNTUTOOLS_BUILDER'] = 'foo' - self.assertEqual(self.get_value('BUILDER'), 'bar') + self._config_files["user"] = "TEST_BUILDER=bar" + os.environ["UBUNTUTOOLS_BUILDER"] = "foo" + self.assertEqual(self.get_value("BUILDER"), "bar") def test_compat_keys(self): - self._config_files['user'] = 'COMPATFOOBAR=bar' - self.assertEqual(self.get_value('QUX', compat_keys=['COMPATFOOBAR']), - 'bar') + self._config_files["user"] = "COMPATFOOBAR=bar" + self.assertEqual(self.get_value("QUX", compat_keys=["COMPATFOOBAR"]), "bar") # errs = Logger.stderr.getvalue().strip() # Logger.stderr = StringIO() # self.assertEqual(len(errs.splitlines()), 1) @@ -145,16 +147,16 @@ REPEAT=yes # r'deprecated.*\bCOMPATFOOBAR\b.*\bTEST_QUX\b') def test_boolean(self): - self._config_files['user'] = "TEST_BOOLEAN=yes" - self.assertEqual(self.get_value('BOOLEAN', boolean=True), True) - self._config_files['user'] = "TEST_BOOLEAN=no" - self.assertEqual(self.get_value('BOOLEAN', boolean=True), False) - self._config_files['user'] = "TEST_BOOLEAN=true" - self.assertEqual(self.get_value('BOOLEAN', boolean=True), None) + self._config_files["user"] = "TEST_BOOLEAN=yes" + self.assertEqual(self.get_value("BOOLEAN", boolean=True), True) + self._config_files["user"] = "TEST_BOOLEAN=no" + self.assertEqual(self.get_value("BOOLEAN", boolean=True), False) + self._config_files["user"] = "TEST_BOOLEAN=true" + self.assertEqual(self.get_value("BOOLEAN", boolean=True), None) def test_nonpackagewide(self): - self._config_files['user'] = 'UBUNTUTOOLS_FOOBAR=a' - self.assertEqual(self.get_value('FOOBAR'), None) + self._config_files["user"] = "UBUNTUTOOLS_FOOBAR=a" + self.assertEqual(self.get_value("FOOBAR"), None) class UbuEmailTestCase(unittest.TestCase): @@ -165,71 +167,72 @@ class UbuEmailTestCase(unittest.TestCase): self.clean_environment() def clean_environment(self): - for k in ('UBUMAIL', 'DEBEMAIL', 'DEBFULLNAME'): + for k in ("UBUMAIL", "DEBEMAIL", "DEBFULLNAME"): if k in os.environ: del os.environ[k] def test_pristine(self): - os.environ['DEBFULLNAME'] = name = 'Joe Developer' - os.environ['DEBEMAIL'] = email = 'joe@example.net' + os.environ["DEBFULLNAME"] = name = "Joe Developer" + os.environ["DEBEMAIL"] = email = "joe@example.net" self.assertEqual(ubu_email(), (name, email)) def test_two_hat(self): - os.environ['DEBFULLNAME'] = name = 'Joe Developer' - os.environ['DEBEMAIL'] = 'joe@debian.org' - os.environ['UBUMAIL'] = email = 'joe@ubuntu.com' + os.environ["DEBFULLNAME"] = name = "Joe Developer" + os.environ["DEBEMAIL"] = "joe@debian.org" + os.environ["UBUMAIL"] = email = "joe@ubuntu.com" self.assertEqual(ubu_email(), (name, email)) - self.assertEqual(os.environ['DEBFULLNAME'], name) - self.assertEqual(os.environ['DEBEMAIL'], email) + self.assertEqual(os.environ["DEBFULLNAME"], name) + self.assertEqual(os.environ["DEBEMAIL"], email) def test_two_hat_cmdlineoverride(self): - os.environ['DEBFULLNAME'] = 'Joe Developer' - os.environ['DEBEMAIL'] = 'joe@debian.org' - os.environ['UBUMAIL'] = 'joe@ubuntu.com' - name = 'Foo Bar' - email = 'joe@example.net' + os.environ["DEBFULLNAME"] = "Joe Developer" + os.environ["DEBEMAIL"] = "joe@debian.org" + os.environ["UBUMAIL"] = "joe@ubuntu.com" + name = "Foo Bar" + email = "joe@example.net" self.assertEqual(ubu_email(name, email), (name, email)) - self.assertEqual(os.environ['DEBFULLNAME'], name) - self.assertEqual(os.environ['DEBEMAIL'], email) + self.assertEqual(os.environ["DEBFULLNAME"], name) + self.assertEqual(os.environ["DEBEMAIL"], email) def test_two_hat_noexport(self): - os.environ['DEBFULLNAME'] = name = 'Joe Developer' - os.environ['DEBEMAIL'] = demail = 'joe@debian.org' - os.environ['UBUMAIL'] = uemail = 'joe@ubuntu.com' + os.environ["DEBFULLNAME"] = name = "Joe Developer" + os.environ["DEBEMAIL"] = demail = "joe@debian.org" + os.environ["UBUMAIL"] = uemail = "joe@ubuntu.com" self.assertEqual(ubu_email(export=False), (name, uemail)) - self.assertEqual(os.environ['DEBFULLNAME'], name) - self.assertEqual(os.environ['DEBEMAIL'], demail) + self.assertEqual(os.environ["DEBFULLNAME"], name) + self.assertEqual(os.environ["DEBEMAIL"], demail) def test_two_hat_with_name(self): - os.environ['DEBFULLNAME'] = 'Joe Developer' - os.environ['DEBEMAIL'] = 'joe@debian.org' - name = 'Joe Ubuntunista' - email = 'joe@ubuntu.com' - os.environ['UBUMAIL'] = '%s <%s>' % (name, email) + os.environ["DEBFULLNAME"] = "Joe Developer" + os.environ["DEBEMAIL"] = "joe@debian.org" + name = "Joe Ubuntunista" + email = "joe@ubuntu.com" + os.environ["UBUMAIL"] = "%s <%s>" % (name, email) self.assertEqual(ubu_email(), (name, email)) - self.assertEqual(os.environ['DEBFULLNAME'], name) - self.assertEqual(os.environ['DEBEMAIL'], email) + self.assertEqual(os.environ["DEBFULLNAME"], name) + self.assertEqual(os.environ["DEBEMAIL"], email) def test_debemail_with_name(self): - name = 'Joe Developer' - email = 'joe@example.net' - os.environ['DEBEMAIL'] = orig = '%s <%s>' % (name, email) + name = "Joe Developer" + email = "joe@example.net" + os.environ["DEBEMAIL"] = orig = "%s <%s>" % (name, email) self.assertEqual(ubu_email(), (name, email)) - self.assertEqual(os.environ['DEBEMAIL'], orig) + self.assertEqual(os.environ["DEBEMAIL"], orig) def test_unicode_name(self): encoding = locale.getdefaultlocale()[1] if not encoding: - encoding = 'utf-8' - name = 'Jöe Déveloper' + encoding = "utf-8" + name = "Jöe Déveloper" env_name = name if isinstance(name, bytes): - name = 'Jöe Déveloper'.decode('utf-8') + name = "Jöe Déveloper".decode("utf-8") env_name = name.encode(encoding) try: - os.environ['DEBFULLNAME'] = env_name + os.environ["DEBFULLNAME"] = env_name except UnicodeEncodeError: - raise unittest.SkipTest("python interpreter is not running in an " - "unicode capable locale") - os.environ['DEBEMAIL'] = email = 'joe@example.net' + raise unittest.SkipTest( + "python interpreter is not running in an unicode capable locale" + ) + os.environ["DEBEMAIL"] = email = "joe@example.net" self.assertEqual(ubu_email(), (name, email)) diff --git a/ubuntutools/test/test_help.py b/ubuntutools/test/test_help.py index 3447f31..03fd956 100644 --- a/ubuntutools/test/test_help.py +++ b/ubuntutools/test/test_help.py @@ -27,10 +27,12 @@ class HelpTestCase(unittest.TestCase): def test_script(self): for script in scripts: with self.subTest(script=script): - result = subprocess.run([f'./{script}', '--help'], - encoding='UTF-8', - timeout=10, - check=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + result = subprocess.run( + [f"./{script}", "--help"], + encoding="UTF-8", + timeout=10, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) self.assertFalse(result.stderr.strip()) diff --git a/ubuntutools/test/test_update_maintainer.py b/ubuntutools/test/test_update_maintainer.py index a7acbb2..5f32f8f 100644 --- a/ubuntutools/test/test_update_maintainer.py +++ b/ubuntutools/test/test_update_maintainer.py @@ -17,7 +17,6 @@ """Test suite for ubuntutools.update_maintainer""" import os -# import sys import unittest from io import StringIO @@ -200,24 +199,23 @@ class UpdateMaintainerTestCase(unittest.TestCase): """TestCase object for ubuntutools.update_maintainer""" _directory = "/" - _files = { - "changelog": None, - "control": None, - "control.in": None, - "rules": None, - } + _files = {"changelog": None, "control": None, "control.in": None, "rules": None} def _fake_isfile(self, filename): """Check only for existing fake files.""" directory, base = os.path.split(filename) - return (directory == self._directory and base in self._files and - self._files[base] is not None) + return ( + directory == self._directory and base in self._files and self._files[base] is not None + ) - def _fake_open(self, filename, mode='r'): + def _fake_open(self, filename, mode="r"): """Provide StringIO objects instead of real files.""" directory, base = os.path.split(filename) - if (directory != self._directory or base not in self._files or - (mode == "r" and self._files[base] is None)): + if ( + directory != self._directory + or base not in self._files + or (mode == "r" and self._files[base] is None) + ): raise IOError("No such file or directory: '%s'" % filename) if mode == "w": self._files[base] = StringIO() @@ -228,11 +226,11 @@ class UpdateMaintainerTestCase(unittest.TestCase): def setUp(self): m = mock.mock_open() m.side_effect = self._fake_open - patcher = mock.patch('builtins.open', m) + patcher = mock.patch("builtins.open", m) self.addCleanup(patcher.stop) patcher.start() m = mock.MagicMock(side_effect=self._fake_isfile) - patcher = mock.patch('os.path.isfile', m) + patcher = mock.patch("os.path.isfile", m) self.addCleanup(patcher.stop) patcher.start() self._files["rules"] = StringIO(_SIMPLE_RULES) @@ -260,8 +258,8 @@ class UpdateMaintainerTestCase(unittest.TestCase): def test_original_ubuntu_maintainer(self): """Test: Original maintainer is Ubuntu developer. - The Maintainer field needs to be update even if - XSBC-Original-Maintainer has an @ubuntu.com address.""" + The Maintainer field needs to be update even if + XSBC-Original-Maintainer has an @ubuntu.com address.""" self._files["changelog"] = StringIO(_LUCID_CHANGELOG) self._files["control"] = StringIO(_AXIS2C_CONTROL) update_maintainer(self._directory) @@ -288,12 +286,11 @@ class UpdateMaintainerTestCase(unittest.TestCase): def test_comments_in_control(self): """Test: Update Maintainer field in a control file containing - comments.""" + comments.""" self._files["changelog"] = StringIO(_LUCID_CHANGELOG) self._files["control"] = StringIO(_SEAHORSE_PLUGINS_CONTROL) update_maintainer(self._directory) - self.assertEqual(self._files["control"].getvalue(), - _SEAHORSE_PLUGINS_UPDATED) + self.assertEqual(self._files["control"].getvalue(), _SEAHORSE_PLUGINS_UPDATED) def test_skip_smart_rules(self): """Test: Skip update when XSBC-Original in debian/rules.""" diff --git a/ubuntutools/update_maintainer.py b/ubuntutools/update_maintainer.py index 89a5e3a..e5856f4 100644 --- a/ubuntutools/update_maintainer.py +++ b/ubuntutools/update_maintainer.py @@ -22,6 +22,7 @@ import re import debian.changelog import logging + Logger = logging.getLogger(__name__) # Prior May 2009 these Maintainers were used: @@ -47,16 +48,16 @@ class Control(object): def get_maintainer(self): """Returns the value of the Maintainer field.""" - maintainer = re.search("^Maintainer: ?(.*)$", self._content, - re.MULTILINE) + maintainer = re.search("^Maintainer: ?(.*)$", self._content, re.MULTILINE) if maintainer: maintainer = maintainer.group(1) return maintainer def get_original_maintainer(self): """Returns the value of the XSBC-Original-Maintainer field.""" - orig_maintainer = re.search("^(?:[XSBC]*-)?Original-Maintainer: ?(.*)$", - self._content, re.MULTILINE) + orig_maintainer = re.search( + "^(?:[XSBC]*-)?Original-Maintainer: ?(.*)$", self._content, re.MULTILINE + ) if orig_maintainer: orig_maintainer = orig_maintainer.group(1) return orig_maintainer @@ -78,25 +79,23 @@ class Control(object): """Sets the value of the XSBC-Original-Maintainer field.""" original_maintainer = "XSBC-Original-Maintainer: " + original_maintainer if self.get_original_maintainer(): - pattern = re.compile("^(?:[XSBC]*-)?Original-Maintainer:.*$", - re.MULTILINE) + pattern = re.compile("^(?:[XSBC]*-)?Original-Maintainer:.*$", re.MULTILINE) self._content = pattern.sub(original_maintainer, self._content) else: pattern = re.compile("^(Maintainer:.*)$", re.MULTILINE) - self._content = pattern.sub(r"\1\n" + original_maintainer, - self._content) + self._content = pattern.sub(r"\1\n" + original_maintainer, self._content) def remove_original_maintainer(self): """Strip out out the XSBC-Original-Maintainer line""" - pattern = re.compile("^(?:[XSBC]*-)?Original-Maintainer:.*?$.*?^", - re.MULTILINE | re.DOTALL) - self._content = pattern.sub('', self._content) + pattern = re.compile( + "^(?:[XSBC]*-)?Original-Maintainer:.*?$.*?^", re.MULTILINE | re.DOTALL + ) + self._content = pattern.sub("", self._content) def _get_distribution(changelog_file): """get distribution of latest changelog entry""" - changelog = debian.changelog.Changelog(open(changelog_file), strict=False, - max_blocks=1) + changelog = debian.changelog.Changelog(open(changelog_file), strict=False, max_blocks=1) distribution = changelog.distributions.split()[0] # Strip things like "-proposed-updates" or "-security" from distribution return distribution.split("-", 1)[0] @@ -107,25 +106,21 @@ def _find_files(debian_directory, verbose): Returns (changelog, control files list) Raises an exception if none can be found. """ - possible_contol_files = [os.path.join(debian_directory, f) for - f in ["control.in", "control"]] + possible_contol_files = [os.path.join(debian_directory, f) for f in ["control.in", "control"]] changelog_file = os.path.join(debian_directory, "changelog") control_files = [f for f in possible_contol_files if os.path.isfile(f)] # Make sure that a changelog and control file is available if len(control_files) == 0: - raise MaintainerUpdateException( - "No control file found in %s." % debian_directory) + raise MaintainerUpdateException("No control file found in %s." % debian_directory) if not os.path.isfile(changelog_file): - raise MaintainerUpdateException( - "No changelog file found in %s." % debian_directory) + raise MaintainerUpdateException("No changelog file found in %s." % debian_directory) # If the rules file accounts for XSBC-Original-Maintainer, we should not # touch it in this package (e.g. the python package). rules_file = os.path.join(debian_directory, "rules") - if os.path.isfile(rules_file) and \ - 'XSBC-Original-' in open(rules_file).read(): + if os.path.isfile(rules_file) and "XSBC-Original-" in open(rules_file).read(): if verbose: print("XSBC-Original is managed by 'rules' file. Doing nothing.") control_files = [] @@ -178,8 +173,9 @@ def update_maintainer(debian_directory, verbose=False): return if control.get_original_maintainer() is not None: - Logger.warning("Overwriting original maintainer: %s", - control.get_original_maintainer()) + Logger.warning( + "Overwriting original maintainer: %s", control.get_original_maintainer() + ) if verbose: print("The original maintainer is: %s" % original_maintainer) diff --git a/ubuntutools/version.py b/ubuntutools/version.py index 2a6569e..0ff59dc 100644 --- a/ubuntutools/version.py +++ b/ubuntutools/version.py @@ -17,28 +17,28 @@ import debian.debian_support class Version(debian.debian_support.Version): def strip_epoch(self): - '''Removes the epoch from a Debian version string. + """Removes the epoch from a Debian version string. strip_epoch(1:1.52-1) will return "1.52-1" and strip_epoch(1.1.3-1) will return "1.1.3-1". - ''' - parts = self.full_version.split(':') + """ + parts = self.full_version.split(":") if len(parts) > 1: del parts[0] - version_without_epoch = ':'.join(parts) + version_without_epoch = ":".join(parts) return version_without_epoch def get_related_debian_version(self): - '''Strip the ubuntu-specific bits off the version''' + """Strip the ubuntu-specific bits off the version""" related_debian_version = self.full_version - uidx = related_debian_version.find('ubuntu') + uidx = related_debian_version.find("ubuntu") if uidx > 0: related_debian_version = related_debian_version[:uidx] - uidx = related_debian_version.find('build') + uidx = related_debian_version.find("build") if uidx > 0: related_debian_version = related_debian_version[:uidx] return Version(related_debian_version) def is_modified_in_ubuntu(self): - '''Did Ubuntu modify this (and mark the version appropriately)?''' - return 'ubuntu' in self.full_version + """Did Ubuntu modify this (and mark the version appropriately)?""" + return "ubuntu" in self.full_version diff --git a/update-maintainer b/update-maintainer index 769d36d..3aa82bf 100755 --- a/update-maintainer +++ b/update-maintainer @@ -18,9 +18,11 @@ import optparse import os import sys -from ubuntutools.update_maintainer import (update_maintainer, - restore_maintainer, - MaintainerUpdateException) +from ubuntutools.update_maintainer import ( + update_maintainer, + restore_maintainer, + MaintainerUpdateException, +) def find_debian_dir(depth=6): @@ -30,10 +32,11 @@ def find_debian_dir(depth=6): :rtype: str :returns: a path to an existing debian/ directory, or None """ - for path in ['../'*n or './' for n in list(range(0, depth+1))]: - debian_path = '{}debian'.format(path) - if os.path.exists(os.path.join(debian_path, 'control')) \ - and os.path.exists(os.path.join(debian_path, 'changelog')): + for path in ["../" * n or "./" for n in list(range(0, depth + 1))]: + debian_path = "{}debian".format(path) + if os.path.exists(os.path.join(debian_path, "control")) and os.path.exists( + os.path.join(debian_path, "changelog") + ): return debian_path return None @@ -43,20 +46,37 @@ def main(): usage = "%s [options]" % (script_name) epilog = "See %s(1) for more info." % (script_name) parser = optparse.OptionParser(usage=usage, epilog=epilog) - parser.add_option("-d", "--debian-directory", dest="debian_directory", - help="location of the 'debian' directory (default: " - "%default).", metavar="PATH", - default=find_debian_dir() or './debian') - parser.add_option("-r", "--restore", - help="Restore the original maintainer", - action='store_true', default=False) - parser.add_option("-q", "--quiet", help="print no informational messages", - dest="quiet", action="store_true", default=False) + parser.add_option( + "-d", + "--debian-directory", + dest="debian_directory", + help="location of the 'debian' directory (default: %default).", + metavar="PATH", + default=find_debian_dir() or "./debian", + ) + parser.add_option( + "-r", + "--restore", + help="Restore the original maintainer", + action="store_true", + default=False, + ) + parser.add_option( + "-q", + "--quiet", + help="print no informational messages", + dest="quiet", + action="store_true", + default=False, + ) (options, args) = parser.parse_args() if len(args) != 0: - print("%s: Error: Unsupported additional parameters specified: %s" - % (script_name, ", ".join(args)), file=sys.stderr) + print( + "%s: Error: Unsupported additional parameters specified: %s" + % (script_name, ", ".join(args)), + file=sys.stderr, + ) sys.exit(1) if not options.restore: