pkgrepo.py revision 1978
3177N/A#!/usr/bin/python2.6
1968N/A#
1968N/A# CDDL HEADER START
1968N/A#
1968N/A# The contents of this file are subject to the terms of the
1968N/A# Common Development and Distribution License (the "License").
1968N/A# You may not use this file except in compliance with the License.
1968N/A#
1968N/A# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1968N/A# or http://www.opensolaris.org/os/licensing.
1968N/A# See the License for the specific language governing permissions
1968N/A# and limitations under the License.
1968N/A#
1968N/A# When distributing Covered Code, include this CDDL HEADER in each
1968N/A# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1968N/A# If applicable, add the following below this CDDL HEADER, with the
1968N/A# fields enclosed by brackets "[]" replaced with your own identifying
1968N/A# information: Portions Copyright [yyyy] [name of copyright owner]
1968N/A#
1968N/A# CDDL HEADER END
1968N/A#
1968N/A
1968N/A#
3158N/A# Copyright (c) 2010 Oracle and/or its affiliates. All rights reserved.
1968N/A#
1968N/A
1968N/APKG_CLIENT_NAME = "pkgrepo"
1968N/A
1968N/A# pkgrepo exit codes
1968N/AEXIT_OK = 0
1968N/AEXIT_OOPS = 1
1968N/AEXIT_BADOPT = 2
1968N/AEXIT_PARTIAL = 3
3210N/A
1968N/A# listing constants
1968N/ALISTING_FORMATS = ("tsv", )
3031N/A
1968N/Aimport errno
3210N/Aimport getopt
3210N/Aimport gettext
3210N/Aimport locale
3210N/Aimport logging
3210N/Aimport os
3210N/Aimport sys
2028N/Aimport urllib
2028N/Aimport urlparse
2028N/Aimport warnings
2028N/A
2301N/Afrom pkg.client import global_settings
2028N/Afrom pkg.misc import msg, PipeError
1968N/Aimport pkg
1968N/Aimport pkg.client.api_errors as apx
1968N/Aimport pkg.client.publisher as publisher
1968N/Aimport pkg.misc as misc
1968N/Aimport pkg.server.repository as sr
3138N/Aimport shlex
3210N/Aimport traceback
2028N/A
2028N/Alogger = global_settings.logger
1968N/A
2028N/Adef error(text, cmd=None):
2843N/A """Emit an error message prefixed by the command name """
2028N/A
1968N/A if cmd:
3041N/A text = "%s: %s" % (cmd, text)
1968N/A pkg_cmd = "pkgrepo "
1968N/A else:
2864N/A pkg_cmd = "pkgrepo: "
1968N/A
3210N/A # If we get passed something like an Exception, we can convert
1968N/A # it down to a string.
2028N/A text = str(text)
1968N/A
2616N/A # If the message starts with whitespace, assume that it should come
2301N/A # *before* the command-name prefix.
1968N/A text_nows = text.lstrip()
2028N/A ws = text[:len(text) - len(text_nows)]
3210N/A
1968N/A # This has to be a constant value as we can't reliably get our actual
1968N/A # program name on all platforms.
3210N/A logger.error(ws + pkg_cmd + text_nows)
1968N/A
1968N/A
2028N/Adef usage(usage_error=None, cmd=None, retcode=2, full=False):
2028N/A """Emit a usage message and optionally prefix it with a more
2028N/A specific error message. Causes program to exit.
2028N/A """
2028N/A
2028N/A if usage_error:
1968N/A error(usage_error, cmd=cmd)
2301N/A
1968N/A if not full:
1968N/A # The full usage message isn't desired.
1968N/A logger.error(_("Try `pkgrepo --help or -?' for more "
1968N/A "information."))
3158N/A sys.exit(retcode)
1968N/A
1968N/A msg(_("""\
1968N/AUsage:
1968N/A pkgrepo [options] command [cmd_options] [operands]
1968N/A
1968N/ASubcommands:
1968N/A pkgrepo create uri_or_path
1968N/A
1968N/A pkgrepo property [-F format] [-H] [<section/property> ...]
1968N/A pkgrepo set-property <section/property>=<value> or
1968N/A <section/property>=(["<value>", ...])
1968N/A
1968N/A pkgrepo publisher [-F format] [-H] [publisher ...]
1968N/A
1968N/A pkgrepo rebuild [--no-index]
1968N/A pkgrepo refresh [--no-catalog] [--no-index]
1968N/A
1968N/A pkgrepo version
2301N/A pkgrepo help
2301N/A
2301N/AOptions:
2301N/A -s repo_uri_or_path
2301N/A A URI or filesystem path representing the location of a package
2301N/A repository. Currently, only filesystem-based repositories are
2301N/A supported.
2301N/A
2301N/A --help or -?
2301N/A Displays a usage message."""))
2301N/A
2301N/A sys.exit(retcode)
2301N/A
1968N/Aclass OptionError(Exception):
1968N/A """Option exception. """
1968N/A
1968N/A def __init__(self, *args):
1968N/A Exception.__init__(self, *args)
1968N/A
1968N/A
1968N/Adef parse_uri(uri):
1968N/A """Parse the repository location provided and attempt to transform it
1968N/A into a valid repository URI.
1968N/A """
1968N/A
1968N/A if uri.find("://") == -1 and not uri.startswith("file:/"):
1968N/A # Convert the file path to a URI.
1968N/A uri = os.path.abspath(uri)
1968N/A uri = urlparse.urlunparse(("file", "",
1978N/A urllib.pathname2url(uri), "", "", ""))
1968N/A
1968N/A scheme, netloc, path, params, query, fragment = \
2510N/A urlparse.urlparse(uri, "file", allow_fragments=0)
2028N/A scheme = scheme.lower()
2301N/A
2142N/A if scheme != "file":
2970N/A usage(_("Network repositories are not currently supported."),
2970N/A retcode=1)
2970N/A
2864N/A if scheme == "file":
2864N/A # During urlunparsing below, ensure that the path starts with
2028N/A # only one '/' character, if any are present.
2864N/A if path.startswith("/"):
2864N/A path = "/" + path.lstrip("/")
1978N/A
2864N/A # Rebuild the url with the sanitized components.
2864N/A uri = urlparse.urlunparse((scheme, netloc, path, params,
2510N/A query, fragment))
3041N/A return publisher.RepositoryURI(uri)
3041N/A
3041N/A
2864N/Adef get_repo(conf, read_only=True, refresh_index=False):
2864N/A """Return the repository object for current program configuration."""
2028N/A
2864N/A repo_uri = conf["repo_root"]
2864N/A path = repo_uri.get_pathname()
2028N/A if not path:
2301N/A # Bad URI?
2301N/A raise sr.RepositoryInvalidError(str(repo_uri))
2301N/A return sr.Repository(auto_create=False, read_only=read_only,
2301N/A refresh_index=refresh_index, repo_root=path)
2028N/A
2028N/A
2028N/Adef subcmd_create(conf, args):
3138N/A """Create a package repository at the given location."""
3138N/A
2843N/A subcommand = "create"
2843N/A opts, pargs = getopt.getopt(args, "")
2843N/A
3210N/A if len(pargs) > 1:
3210N/A usage(_("Only one repository location may be specified."),
3210N/A cmd=subcommand)
3210N/A elif pargs:
2028N/A conf["repo_root"] = parse_uri(pargs[0])
2028N/A
1968N/A repo_root = conf.get("repo_root", None)
1968N/A if not repo_root:
1978N/A usage(_("No repository location specified."), cmd=subcommand)
1978N/A
1968N/A # Attempt to create a repository at the specified location. Allow
1968N/A # whatever exceptions are raised to bubble up.
1968N/A sr.repository_create(repo_root)
3041N/A
1968N/A return EXIT_OK
1968N/A
1968N/A
1968N/Adef print_col_listing(desired_field_order, field_data, field_values, out_format,
1968N/A def_fmt, omit_headers):
1968N/A """Print a columnar listing defined by provided values."""
1968N/A
1968N/A # Custom sort function for preserving field ordering
1968N/A def sort_fields(one, two):
1968N/A return desired_field_order.index(get_header(one)) - \
1968N/A desired_field_order.index(get_header(two))
1968N/A
2028N/A # Functions for manipulating field_data records
1968N/A def filter_default(record):
1968N/A return "default" in record[0]
2301N/A
2301N/A def filter_tsv(record):
2301N/A return "tsv" in record[0]
2301N/A
2301N/A def get_header(record):
2301N/A return record[1]
2301N/A
2301N/A def get_value(record):
2301N/A return record[2]
2301N/A
2301N/A def set_value(entry):
2301N/A entry[0][2] = entry[1]
2301N/A
2301N/A if out_format == "default":
2301N/A # Create a formatting string for the default output
2301N/A # format.
2301N/A fmt = def_fmt
2301N/A filter_func = filter_default
2301N/A elif out_format == "tsv":
2301N/A # Create a formatting string for the tsv output
2301N/A # format.
2301N/A num_fields = len(field_data.keys())
2301N/A fmt = "\t".join('%s' for x in xrange(num_fields))
2301N/A filter_func = filter_tsv
2301N/A
2301N/A # Extract the list of headers from the field_data dictionary. Ensure
2301N/A # they are extracted in the desired order by using the custom sort
2301N/A # function.
2301N/A hdrs = map(get_header, sorted(filter(filter_func, field_data.values()),
2301N/A sort_fields))
2301N/A
3171N/A # Output a header if desired.
2301N/A if not omit_headers:
2301N/A msg(fmt % tuple(hdrs))
2301N/A
2301N/A for entry in field_values:
2301N/A map(set_value, (
2301N/A (field_data[f], v)
2301N/A for f, v in entry.iteritems()
2301N/A ))
3158N/A values = map(get_value, sorted(filter(filter_func,
3158N/A field_data.values()), sort_fields))
2958N/A msg(fmt % tuple(values))
3158N/A
3158N/A
2301N/Adef subcmd_property(conf, args):
2301N/A """Display the list of properties for the repository."""
2301N/A
2301N/A subcommand = "property"
2301N/A repo = get_repo(conf)
2301N/A
2301N/A omit_headers = False
2301N/A out_format = "default"
2301N/A
3158N/A opts, pargs = getopt.getopt(args, "F:H")
3158N/A for opt, arg in opts:
2301N/A if opt == "-F":
2301N/A out_format = arg
2301N/A if out_format not in LISTING_FORMATS:
2301N/A usage(_("Unrecognized format %(format)s."
2301N/A " Supported formats: %(valid)s") % \
2301N/A { "format": out_format,
2301N/A "valid": LISTING_FORMATS }, cmd="publisher")
2301N/A return EXIT_OOPS
2301N/A elif opt == "-H":
2990N/A omit_headers = True
2990N/A
2990N/A # Configuration index is indexed by section name and property name.
2990N/A # Flatten it to simplify listing process.
2990N/A cfg_idx = repo.cfg.get_index()
2028N/A props = set()
2028N/A
2028N/A # Set minimum widths for section and property name columns by using the
2028N/A # length of the column headers.
2028N/A max_sname_len = len(_("SECTION"))
2028N/A max_pname_len = len(_("PROPERTY"))
2028N/A
2028N/A for sname in cfg_idx:
2028N/A max_sname_len = max(max_sname_len, len(sname))
2028N/A for pname in cfg_idx[sname]:
2990N/A max_pname_len = max(max_pname_len, len(pname))
2990N/A props.add("/".join((sname, pname)))
2028N/A del cfg_idx
2028N/A
3210N/A if len(pargs) >= 1:
3140N/A found = props & set(pargs)
2028N/A notfound = set(pargs) - found
2028N/A del props
2028N/A else:
3041N/A found = props
2028N/A notfound = set()
2028N/A
2028N/A def gen_listing():
2028N/A for prop in sorted(found):
2028N/A sname, pname = prop.rsplit("/", 1)
2028N/A sval = str(repo.cfg.get_property(sname, pname))
2028N/A yield {
2028N/A "section": sname,
2028N/A "property": pname,
2028N/A "value": sval,
2028N/A }
2028N/A
2028N/A # SECTION PROPERTY VALUE
2073N/A # <sec_1> <prop_1> <prop_1_value>
2073N/A # <sec_2> <prop_2> <prop_2_value>
3041N/A # ...
2028N/A field_data = {
3140N/A "section" : [("default", "tsv"), _("SECTION"), ""],
3140N/A "property" : [("default", "tsv"), _("PROPERTY"), ""],
3140N/A "value" : [("default", "tsv"), _("VALUE"), ""],
3140N/A }
3140N/A desired_field_order = (_("SECTION"), "", _("PROPERTY"), _("VALUE"))
2028N/A
3140N/A # Default output formatting.
3140N/A def_fmt = "%-" + str(max_sname_len) + "s %-" + str(max_pname_len) + \
3140N/A "s %s"
2028N/A
2028N/A if found or (not pargs and out_format == "default"):
2028N/A print_col_listing(desired_field_order, field_data,
2142N/A gen_listing(), out_format, def_fmt, omit_headers)
2142N/A
2142N/A if found and notfound:
2142N/A return EXIT_PARTIAL
2142N/A if pargs and not found:
2142N/A if out_format == "default":
2142N/A # Don't pollute other output formats.
2142N/A error(_("no matching properties found"),
2142N/A cmd=subcommand)
2142N/A return EXIT_OOPS
2142N/A return EXIT_OK
2142N/A
2142N/A
2142N/Adef subcmd_set_property(conf, args):
2142N/A """Set a repository property."""
2142N/A
2142N/A subcommand = "property"
2142N/A repo = get_repo(conf, read_only=False)
2142N/A
2142N/A omit_headers = False
2142N/A out_format = "default"
2142N/A
2142N/A opts, pargs = getopt.getopt(args, "")
2142N/A bad_args = False
3158N/A if not pargs or len(pargs) > 1:
2142N/A bad_args = True
2142N/A else:
2142N/A try:
2142N/A if len(pargs) == 1:
2142N/A prop, val = pargs[0].split("=", 1)
2142N/A sname, pname = prop.rsplit("/", 1)
2142N/A except ValueError:
2142N/A bad_args = True
2142N/A
2142N/A if bad_args:
2142N/A usage(_("a property name and value must be provided in the "
2142N/A "form <section/property>=<value> or "
2142N/A "<section/property>=([\"<value>\", ...])"))
2142N/A
2142N/A if len(val) > 0 and val[0] == "(" and val[-1] == ")":
2142N/A val = shlex.split(val.strip("()"))
2142N/A
2142N/A repo.cfg.set_property(sname, pname, val)
2142N/A repo.write_config()
2142N/A
2142N/A
2142N/Adef subcmd_publisher(conf, args):
2142N/A """Display a list of known publishers and a summary of known packages
2142N/A and when the package data for the given publisher was last updated.
2142N/A """
2142N/A
2142N/A subcommand = "publisher"
2142N/A repo = get_repo(conf)
2142N/A
3158N/A omit_headers = False
3158N/A out_format = "default"
2142N/A
2142N/A opts, pargs = getopt.getopt(args, "F:H")
2142N/A for opt, arg in opts:
2142N/A if opt == "-F":
2142N/A out_format = arg
2970N/A if out_format not in LISTING_FORMATS:
2970N/A usage(_("Unrecognized format %(format)s."
2970N/A " Supported formats: %(valid)s") % \
2970N/A { "format": out_format,
2970N/A "valid": LISTING_FORMATS }, cmd="publisher")
2970N/A return EXIT_OOPS
2970N/A elif opt == "-H":
2970N/A omit_headers = True
2970N/A
2970N/A cat = repo.catalog
2970N/A pub_idx = {}
2970N/A for pub, pkg_count, pkg_ver_count in cat.get_package_counts_by_pub():
2970N/A pub_idx[pub] = (pkg_count, pkg_ver_count)
2970N/A
2970N/A if len(pargs) >= 1:
2970N/A found = set(pub_idx.keys()) & set(pargs)
2970N/A notfound = set(pargs) - found
2970N/A else:
2970N/A found = set(pub_idx.keys())
2970N/A notfound = set()
2970N/A
2970N/A def gen_listing():
2970N/A for pfx in found:
2970N/A pkg_count, pkg_ver_count = pub_idx[pfx]
2970N/A yield {
2970N/A "publisher": pfx,
2970N/A "packages": pkg_count,
2970N/A "versions": pkg_ver_count,
2970N/A "updated": "%sZ" % cat.last_modified.isoformat(),
2970N/A }
2970N/A
2970N/A # PUBLISHER PACKAGES VERSIONS UPDATED
3158N/A # <pub_1> <num_uniq_pkgs> <num_pkg_vers> <cat_last_modified>
3158N/A # <pub_2> <num_uniq_pkgs> <num_pkg_vers> <cat_last_modified>
2970N/A # ...
2970N/A
2970N/A field_data = {
2970N/A "publisher" : [("default", "tsv"), _("PUBLISHER"), ""],
2970N/A "packages" : [("default", "tsv"), _("PACKAGES"), ""],
2970N/A "versions" : [("default", "tsv"), _("VERSIONS"), ""],
2970N/A "updated" : [("default", "tsv"), _("UPDATED"), ""],
2970N/A }
2970N/A
2970N/A desired_field_order = (_("PUBLISHER"), "", _("PACKAGES"), _("VERSIONS"),
3158N/A _("UPDATED"))
3158N/A
2970N/A # Default output formatting.
2970N/A def_fmt = "%-24s %-8s %-8s %s"
2970N/A
2970N/A if found or (not pargs and out_format == "default"):
2970N/A print_col_listing(desired_field_order, field_data,
2970N/A gen_listing(), out_format, def_fmt, omit_headers)
3158N/A
3158N/A if found and notfound:
2970N/A return EXIT_PARTIAL
2970N/A if pargs and not found:
2970N/A if out_format == "default":
2970N/A # Don't pollute other output formats.
2970N/A error(_("no matching publishers found"),
2970N/A cmd=subcommand)
2970N/A return EXIT_OOPS
2970N/A return EXIT_OK
2970N/A
2970N/A
2970N/Adef subcmd_rebuild(conf, args):
2970N/A """Rebuild the repository's catalog and index data (as permitted)."""
2970N/A
2970N/A subcommand = "rebuild"
3158N/A repo = get_repo(conf, read_only=False)
3158N/A
2970N/A build_index = True
2970N/A opts, pargs = getopt.getopt(args, "", ["no-index"])
2970N/A for opt, arg in opts:
2970N/A if opt == "--no-index":
2970N/A build_index = False
2970N/A
2970N/A if pargs:
2970N/A usage(_("command does not take operands"), cmd=subcommand)
2142N/A
2028N/A logger.info("Rebuilding package repository...")
2028N/A repo.rebuild(build_index=False)
2028N/A
2028N/A if build_index:
2028N/A # Always build search indexes seperately (and if permitted).
2028N/A logger.info("Building search indexes...")
2028N/A repo.refresh_index()
2028N/A
2028N/A return EXIT_OK
2028N/A
2028N/A
2028N/Adef subcmd_refresh(conf, args):
2028N/A """Refresh the repository's catalog and index data (as permitted)."""
2028N/A
2028N/A subcommand = "refresh"
2028N/A repo = get_repo(conf, read_only=False)
2028N/A
2028N/A add_content = True
2028N/A refresh_index = True
1968N/A opts, pargs = getopt.getopt(args, "", ["no-catalog", "no-index"])
2028N/A for opt, arg in opts:
2028N/A if opt == "--no-catalog":
2028N/A add_content = False
2028N/A elif opt == "--no-index":
2028N/A refresh_index = False
2028N/A
2028N/A if pargs:
2028N/A usage(_("command does not take operands"), cmd=subcommand)
2028N/A
2028N/A if not add_content and not refresh_index:
2028N/A # Why? Who knows; but do what was requested--nothing!
2028N/A return EXIT_OK
2028N/A
2028N/A if add_content:
2028N/A logger.info("Adding new package content...")
2028N/A repo.add_content(refresh_index=False)
2028N/A
2028N/A if refresh_index:
2028N/A # Always update search indexes separately (and if permitted).
2028N/A logger.info("Updating search indexes...")
2028N/A repo.refresh_index()
2028N/A
2028N/A return EXIT_OK
2028N/A
1968N/A
1968N/Adef subcmd_version(conf, args):
2028N/A """Display the version of the pkg(5) API."""
2864N/A
2864N/A subcommand = "version"
1968N/A
2864N/A if conf.get("repo_root", None):
1968N/A usage(_("-s not allowed for %s subcommand") %
1968N/A subcommand)
3031N/A if args:
3031N/A usage(_("command does not take operands"), cmd=subcommand)
3031N/A msg(pkg.VERSION)
3031N/A return EXIT_OK
1968N/A
1968N/A
1968N/Adef main_func():
2028N/A global_settings.client_name = PKG_CLIENT_NAME
2028N/A
2028N/A try:
2028N/A opts, pargs = getopt.getopt(sys.argv[1:], "s:?",
2864N/A ["help"])
2864N/A except getopt.GetoptError, e:
2864N/A usage(_("illegal global option -- %s") % e.opt)
2864N/A
2028N/A conf = {}
2028N/A show_usage = False
2028N/A for opt, arg in opts:
2028N/A if opt == "-s":
2028N/A if not arg:
3210N/A continue
3210N/A conf["repo_root"] = parse_uri(arg)
2028N/A elif opt in ("--help", "-?"):
2028N/A show_usage = True
2028N/A
2028N/A subcommand = None
2028N/A if pargs:
2028N/A subcommand = pargs.pop(0)
2028N/A if subcommand == "help":
2028N/A show_usage = True
2028N/A
2028N/A if show_usage:
2028N/A usage(retcode=0, full=True)
1968N/A elif not subcommand:
1968N/A usage(_("no subcommand specified"))
2028N/A
2028N/A subcommand = subcommand.replace("-", "_")
2028N/A func = globals().get("subcmd_%s" % subcommand, None)
1968N/A if not func:
1968N/A usage(_("unknown subcommand '%s'") % subcommand)
1968N/A
1968N/A try:
1968N/A if (subcommand != "create" and subcommand != "version") and \
1968N/A not conf.get("repo_root", None):
1968N/A usage(_("A package repository location must be "
1968N/A "provided using -s."), cmd=subcommand)
1968N/A return func(conf, pargs)
1968N/A except getopt.GetoptError, e:
1968N/A if e.opt in ("help", "?"):
1968N/A usage(full=True)
1968N/A usage(_("illegal option -- %s") % e.opt, cmd=subcommand)
2028N/A
2028N/A#
2028N/A# Establish a specific exit status which means: "python barfed an exception"
2028N/A# so that we can more easily detect these in testing of the CLI commands.
1968N/A#
1968N/Adef handle_errors(func, *args, **kwargs):
1968N/A """Catch exceptions raised by the main program function and then print
1968N/A a message and/or exit with an appropriate return code.
1968N/A """
1968N/A
1968N/A traceback_str = _("\n\nThis is an internal error. Please let the "
1968N/A "developers know about this\nproblem by filing a bug at "
2028N/A "http://defect.opensolaris.org and including the\nabove "
1968N/A "traceback and this message. The version of pkg(5) is "
1968N/A "'%s'.") % pkg.VERSION
1968N/A try:
1968N/A # Out of memory errors can be raised as EnvironmentErrors with
1968N/A # an errno of ENOMEM, so in order to handle those exceptions
1968N/A # with other errnos, we nest this try block and have the outer
1968N/A # one handle the other instances.
1968N/A try:
1968N/A __ret = func(*args, **kwargs)
1968N/A except (MemoryError, EnvironmentError), __e:
1968N/A if isinstance(__e, EnvironmentError) and \
2510N/A __e.errno != errno.ENOMEM:
2510N/A raise
2510N/A error("\n" + misc.out_of_memory())
1968N/A __ret = EXIT_OOPS
2510N/A except SystemExit, __e:
1968N/A raise __e
1968N/A except (PipeError, KeyboardInterrupt):
3158N/A # Don't display any messages here to prevent possible further
3158N/A # broken pipe (EPIPE) errors.
1968N/A __ret = EXIT_OOPS
2028N/A except apx.VersionException, __e:
2453N/A error(_("The pkgrepo command appears out of sync with the "
2510N/A "libraries provided\nby pkg:/package/pkg. The client "
2453N/A "version is %(client)s while the library\nAPI version is "
2453N/A "%(api)s.") % {'client': __e.received_version,
1968N/A 'api': __e.expected_version
1968N/A })
1968N/A __ret = EXIT_OOPS
2028N/A except (apx.ApiException, sr.RepositoryError), __e:
1968N/A error(str(__e))
1968N/A __ret = EXIT_OOPS
1968N/A except:
1968N/A traceback.print_exc()
1968N/A error(traceback_str)
1968N/A __ret = 99
1968N/A return __ret
1968N/A
2510N/A
3210N/Aif __name__ == "__main__":
1968N/A misc.setlocale(locale.LC_ALL, "", error)
2028N/A gettext.install("pkg", "/usr/share/locale")
2028N/A
2028N/A # Make all warnings be errors.
2028N/A warnings.simplefilter('error')
2028N/A
2028N/A __retval = handle_errors(main_func)
2510N/A try:
1968N/A logging.shutdown()
2028N/A except IOError:
2028N/A # Ignore python's spurious pipe problems.
2028N/A pass
2510N/A sys.exit(__retval)
2510N/A