3542 lines
126 KiB
Diff
3542 lines
126 KiB
Diff
From 3b70442331338696847abc23b8e0d8d4f9618bba Mon Sep 17 00:00:00 2001
|
|
From: Tomas Kasparek <tkasparek@redhat.com>
|
|
Date: Mon, 5 Mar 2018 11:06:22 +0100
|
|
Subject: [PATCH 02/17] cleanup ANCIENT_PYTHON stuff and unused imports
|
|
|
|
---
|
|
koan/app.py | 98 +--
|
|
koan/opt_parse.py | 1682 ---------------------------------------------------
|
|
koan/register.py | 13 +-
|
|
koan/sub_process.py | 1149 -----------------------------------
|
|
koan/text_wrap.py | 354 -----------
|
|
koan/utils.py | 50 +-
|
|
koan/vmwcreate.py | 6 +-
|
|
7 files changed, 39 insertions(+), 3313 deletions(-)
|
|
delete mode 100644 koan/opt_parse.py
|
|
delete mode 100644 koan/sub_process.py
|
|
delete mode 100644 koan/text_wrap.py
|
|
|
|
diff --git a/koan/app.py b/koan/app.py
|
|
index d911dad..801fedd 100755
|
|
--- a/koan/app.py
|
|
+++ b/koan/app.py
|
|
@@ -27,40 +27,19 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
import random
|
|
import os
|
|
import traceback
|
|
-import tempfile
|
|
import shlex
|
|
|
|
-ANCIENT_PYTHON = 0
|
|
-try:
|
|
- try:
|
|
- from optparse import OptionParser
|
|
- except:
|
|
- from opt_parse import OptionParser # importing this for backwards compat with 2.2
|
|
- try:
|
|
- import subprocess as sub_process
|
|
- except:
|
|
- import sub_process
|
|
-except:
|
|
- # the main "replace-self" codepath of koan must support
|
|
- # Python 1.5. Other sections may use 2.3 features (nothing newer)
|
|
- # provided they are conditionally imported. This is to support
|
|
- # EL 2.1. -- mpd
|
|
- ANCIENT_PYTHON = 1
|
|
- True = 1
|
|
- False = 0
|
|
+from optparse import OptionParser
|
|
+import subprocess as sub_process
|
|
|
|
import time
|
|
import shutil
|
|
import errno
|
|
-import re
|
|
import sys
|
|
import xmlrpclib
|
|
import string
|
|
import re
|
|
-import glob
|
|
-import socket
|
|
import utils
|
|
-import time
|
|
|
|
COBBLER_REQUIRED = 1.300
|
|
|
|
@@ -94,11 +73,6 @@ def main():
|
|
# most likely running RHEL3, where we don't need virt logging anyway
|
|
pass
|
|
|
|
- if ANCIENT_PYTHON:
|
|
- print "- command line usage on this version of python is unsupported"
|
|
- print "- usage via spacewalk APIs only. Python x>=2.3 required"
|
|
- return
|
|
-
|
|
p = OptionParser()
|
|
p.add_option("-k", "--kopts",
|
|
dest="kopts_override",
|
|
@@ -538,16 +512,15 @@ class Koan:
|
|
else:
|
|
# FIXME: auto never selects vmware, maybe it should if we find it?
|
|
|
|
- if not ANCIENT_PYTHON:
|
|
- cmd = sub_process.Popen("/bin/uname -r", stdout=sub_process.PIPE, shell=True)
|
|
- uname_str = cmd.communicate()[0]
|
|
- if uname_str.find("xen") != -1:
|
|
- self.virt_type = "xenpv"
|
|
- elif os.path.exists("/usr/bin/qemu-img"):
|
|
- self.virt_type = "qemu"
|
|
- else:
|
|
- # assume Xen, we'll check to see if virt-type is really usable later.
|
|
- raise InfoException, "Not running a Xen kernel and qemu is not installed"
|
|
+ cmd = sub_process.Popen("/bin/uname -r", stdout=sub_process.PIPE, shell=True)
|
|
+ uname_str = cmd.communicate()[0]
|
|
+ if uname_str.find("xen") != -1:
|
|
+ self.virt_type = "xenpv"
|
|
+ elif os.path.exists("/usr/bin/qemu-img"):
|
|
+ self.virt_type = "qemu"
|
|
+ else:
|
|
+ # assume Xen, we'll check to see if virt-type is really usable later.
|
|
+ raise InfoException, "Not running a Xen kernel and qemu is not installed"
|
|
|
|
print "- no virt-type specified, auto-selecting %s" % self.virt_type
|
|
|
|
@@ -811,12 +784,6 @@ class Koan:
|
|
#---------------------------------------------------
|
|
|
|
def get_boot_loader_info(self):
|
|
- if ANCIENT_PYTHON:
|
|
- # FIXME: implement this to work w/o subprocess
|
|
- if os.path.exists("/etc/grub.conf"):
|
|
- return (0, "grub")
|
|
- else:
|
|
- return (0, "lilo")
|
|
cmd = [ "/sbin/grubby", "--bootloader-probe" ]
|
|
probe_process = sub_process.Popen(cmd, stdout=sub_process.PIPE)
|
|
which_loader = probe_process.communicate()[0]
|
|
@@ -862,11 +829,8 @@ class Koan:
|
|
profile_data
|
|
)
|
|
|
|
- if not ANCIENT_PYTHON:
|
|
- arch_cmd = sub_process.Popen("/bin/uname -m", stdout=sub_process.PIPE, shell=True)
|
|
- arch = arch_cmd.communicate()[0]
|
|
- else:
|
|
- arch = "i386"
|
|
+ arch_cmd = sub_process.Popen("/bin/uname -m", stdout=sub_process.PIPE, shell=True)
|
|
+ arch = arch_cmd.communicate()[0]
|
|
|
|
# Validate kernel argument length (limit depends on architecture --
|
|
# see asm-*/setup.h). For example:
|
|
@@ -875,15 +839,14 @@ class Koan:
|
|
# asm-powerpc/setup.h:#define COMMAND_LINE_SIZE 512
|
|
# asm-s390/setup.h:#define COMMAND_LINE_SIZE 896
|
|
# asm-x86_64/setup.h:#define COMMAND_LINE_SIZE 2048
|
|
- if not ANCIENT_PYTHON:
|
|
- if arch.startswith("ppc") or arch.startswith("ia64"):
|
|
- if len(k_args) > 511:
|
|
- raise InfoException, "Kernel options are too long, 512 chars exceeded: %s" % k_args
|
|
- elif arch.startswith("s390"):
|
|
- if len(k_args) > 895:
|
|
- raise InfoException, "Kernel options are too long, 896 chars exceeded: %s" % k_args
|
|
- elif len(k_args) > 2047:
|
|
- raise InfoException, "Kernel options are too long, 2047 chars exceeded: %s" % k_args
|
|
+ if arch.startswith("ppc") or arch.startswith("ia64"):
|
|
+ if len(k_args) > 511:
|
|
+ raise InfoException, "Kernel options are too long, 512 chars exceeded: %s" % k_args
|
|
+ elif arch.startswith("s390"):
|
|
+ if len(k_args) > 895:
|
|
+ raise InfoException, "Kernel options are too long, 896 chars exceeded: %s" % k_args
|
|
+ elif len(k_args) > 2047:
|
|
+ raise InfoException, "Kernel options are too long, 2047 chars exceeded: %s" % k_args
|
|
|
|
cmd = [ "/sbin/grubby",
|
|
"--add-kernel", self.safe_load(profile_data,'kernel_local'),
|
|
@@ -910,14 +873,13 @@ class Koan:
|
|
cmd.append("--config-file=/tmp/boot/boot/grub/grub.conf")
|
|
|
|
# Are we running on ppc?
|
|
- if not ANCIENT_PYTHON:
|
|
- if arch.startswith("ppc"):
|
|
- if "grub2" in probe_output:
|
|
- cmd.append("--grub2")
|
|
- else:
|
|
- cmd.append("--yaboot")
|
|
- elif arch.startswith("s390"):
|
|
- cmd.append("--zipl")
|
|
+ if arch.startswith("ppc"):
|
|
+ if "grub2" in probe_output:
|
|
+ cmd.append("--grub2")
|
|
+ else:
|
|
+ cmd.append("--yaboot")
|
|
+ elif arch.startswith("s390"):
|
|
+ cmd.append("--zipl")
|
|
|
|
utils.subprocess_call(cmd)
|
|
|
|
@@ -933,12 +895,12 @@ class Koan:
|
|
utils.subprocess_call(cmd)
|
|
|
|
# Any post-grubby processing required (e.g. ybin, zipl, lilo)?
|
|
- if not ANCIENT_PYTHON and arch.startswith("ppc") and "grub2" not in probe_output:
|
|
+ if arch.startswith("ppc") and "grub2" not in probe_output:
|
|
# FIXME - CHRP hardware uses a 'PPC PReP Boot' partition and doesn't require running ybin
|
|
print "- applying ybin changes"
|
|
cmd = [ "/sbin/ybin" ]
|
|
utils.subprocess_call(cmd)
|
|
- elif not ANCIENT_PYTHON and arch.startswith("s390"):
|
|
+ elif arch.startswith("s390"):
|
|
print "- applying zipl changes"
|
|
cmd = [ "/sbin/zipl" ]
|
|
utils.subprocess_call(cmd)
|
|
diff --git a/koan/opt_parse.py b/koan/opt_parse.py
|
|
deleted file mode 100644
|
|
index 233d192..0000000
|
|
--- a/koan/opt_parse.py
|
|
+++ /dev/null
|
|
@@ -1,1682 +0,0 @@
|
|
-"""optparse - a powerful, extensible, and easy-to-use option parser.
|
|
-
|
|
-By Greg Ward <gward@python.net>
|
|
-
|
|
-Originally distributed as Optik; see http://optik.sourceforge.net/ .
|
|
-
|
|
-If you have problems with this module, please do not file bugs,
|
|
-patches, or feature requests with Python; instead, use Optik's
|
|
-SourceForge project page:
|
|
- http://sourceforge.net/projects/optik
|
|
-
|
|
-For support, use the optik-users@lists.sourceforge.net mailing list
|
|
-(http://lists.sourceforge.net/lists/listinfo/optik-users).
|
|
-"""
|
|
-
|
|
-# Python developers: please do not make changes to this file, since
|
|
-# it is automatically generated from the Optik source code.
|
|
-
|
|
-__version__ = "1.5.3"
|
|
-
|
|
-__all__ = ['Option',
|
|
- 'SUPPRESS_HELP',
|
|
- 'SUPPRESS_USAGE',
|
|
- 'Values',
|
|
- 'OptionContainer',
|
|
- 'OptionGroup',
|
|
- 'OptionParser',
|
|
- 'HelpFormatter',
|
|
- 'IndentedHelpFormatter',
|
|
- 'TitledHelpFormatter',
|
|
- 'OptParseError',
|
|
- 'OptionError',
|
|
- 'OptionConflictError',
|
|
- 'OptionValueError',
|
|
- 'BadOptionError']
|
|
-
|
|
-__copyright__ = """
|
|
-Copyright (c) 2001-2006 Gregory P. Ward. All rights reserved.
|
|
-Copyright (c) 2002-2006 Python Software Foundation. All rights reserved.
|
|
-
|
|
-Redistribution and use in source and binary forms, with or without
|
|
-modification, are permitted provided that the following conditions are
|
|
-met:
|
|
-
|
|
- * Redistributions of source code must retain the above copyright
|
|
- notice, this list of conditions and the following disclaimer.
|
|
-
|
|
- * Redistributions in binary form must reproduce the above copyright
|
|
- notice, this list of conditions and the following disclaimer in the
|
|
- documentation and/or other materials provided with the distribution.
|
|
-
|
|
- * Neither the name of the author nor the names of its
|
|
- contributors may be used to endorse or promote products derived from
|
|
- this software without specific prior written permission.
|
|
-
|
|
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
-IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
|
-PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
|
|
-CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
-EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
-PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
-LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
-NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
-"""
|
|
-
|
|
-import sys, os
|
|
-import types
|
|
-import text_wrap as textwrap # repackaged here because not in RHEL3
|
|
-
|
|
-def _repr(self):
|
|
- return "<%s at 0x%x: %s>" % (self.__class__.__name__, id(self), self)
|
|
-
|
|
-
|
|
-# This file was generated from:
|
|
-# Id: option_parser.py 527 2006-07-23 15:21:30Z greg
|
|
-# Id: option.py 522 2006-06-11 16:22:03Z gward
|
|
-# Id: help.py 527 2006-07-23 15:21:30Z greg
|
|
-# Id: errors.py 509 2006-04-20 00:58:24Z gward
|
|
-
|
|
-try:
|
|
- from gettext import gettext
|
|
-except ImportError:
|
|
- def gettext(message):
|
|
- return message
|
|
-_ = gettext
|
|
-
|
|
-
|
|
-class OptParseError (Exception):
|
|
- def __init__(self, msg):
|
|
- self.msg = msg
|
|
-
|
|
- def __str__(self):
|
|
- return self.msg
|
|
-
|
|
-
|
|
-class OptionError (OptParseError):
|
|
- """
|
|
- Raised if an Option instance is created with invalid or
|
|
- inconsistent arguments.
|
|
- """
|
|
-
|
|
- def __init__(self, msg, option):
|
|
- self.msg = msg
|
|
- self.option_id = str(option)
|
|
-
|
|
- def __str__(self):
|
|
- if self.option_id:
|
|
- return "option %s: %s" % (self.option_id, self.msg)
|
|
- else:
|
|
- return self.msg
|
|
-
|
|
-class OptionConflictError (OptionError):
|
|
- """
|
|
- Raised if conflicting options are added to an OptionParser.
|
|
- """
|
|
-
|
|
-class OptionValueError (OptParseError):
|
|
- """
|
|
- Raised if an invalid option value is encountered on the command
|
|
- line.
|
|
- """
|
|
-
|
|
-class BadOptionError (OptParseError):
|
|
- """
|
|
- Raised if an invalid option is seen on the command line.
|
|
- """
|
|
- def __init__(self, opt_str):
|
|
- self.opt_str = opt_str
|
|
-
|
|
- def __str__(self):
|
|
- return _("no such option: %s") % self.opt_str
|
|
-
|
|
-class AmbiguousOptionError (BadOptionError):
|
|
- """
|
|
- Raised if an ambiguous option is seen on the command line.
|
|
- """
|
|
- def __init__(self, opt_str, possibilities):
|
|
- BadOptionError.__init__(self, opt_str)
|
|
- self.possibilities = possibilities
|
|
-
|
|
- def __str__(self):
|
|
- return (_("ambiguous option: %s (%s?)")
|
|
- % (self.opt_str, ", ".join(self.possibilities)))
|
|
-
|
|
-
|
|
-class HelpFormatter:
|
|
-
|
|
- """
|
|
- Abstract base class for formatting option help. OptionParser
|
|
- instances should use one of the HelpFormatter subclasses for
|
|
- formatting help; by default IndentedHelpFormatter is used.
|
|
-
|
|
- Instance attributes:
|
|
- parser : OptionParser
|
|
- the controlling OptionParser instance
|
|
- indent_increment : int
|
|
- the number of columns to indent per nesting level
|
|
- max_help_position : int
|
|
- the maximum starting column for option help text
|
|
- help_position : int
|
|
- the calculated starting column for option help text;
|
|
- initially the same as the maximum
|
|
- width : int
|
|
- total number of columns for output (pass None to constructor for
|
|
- this value to be taken from the $COLUMNS environment variable)
|
|
- level : int
|
|
- current indentation level
|
|
- current_indent : int
|
|
- current indentation level (in columns)
|
|
- help_width : int
|
|
- number of columns available for option help text (calculated)
|
|
- default_tag : str
|
|
- text to replace with each option's default value, "%default"
|
|
- by default. Set to false value to disable default value expansion.
|
|
- option_strings : { Option : str }
|
|
- maps Option instances to the snippet of help text explaining
|
|
- the syntax of that option, e.g. "-h, --help" or
|
|
- "-fFILE, --file=FILE"
|
|
- _short_opt_fmt : str
|
|
- format string controlling how short options with values are
|
|
- printed in help text. Must be either "%s%s" ("-fFILE") or
|
|
- "%s %s" ("-f FILE"), because those are the two syntaxes that
|
|
- Optik supports.
|
|
- _long_opt_fmt : str
|
|
- similar but for long options; must be either "%s %s" ("--file FILE")
|
|
- or "%s=%s" ("--file=FILE").
|
|
- """
|
|
-
|
|
- NO_DEFAULT_VALUE = "none"
|
|
-
|
|
- def __init__(self,
|
|
- indent_increment,
|
|
- max_help_position,
|
|
- width,
|
|
- short_first):
|
|
- self.parser = None
|
|
- self.indent_increment = indent_increment
|
|
- self.help_position = self.max_help_position = max_help_position
|
|
- if width is None:
|
|
- try:
|
|
- width = int(os.environ['COLUMNS'])
|
|
- except (KeyError, ValueError):
|
|
- width = 80
|
|
- width -= 2
|
|
- self.width = width
|
|
- self.current_indent = 0
|
|
- self.level = 0
|
|
- self.help_width = None # computed later
|
|
- self.short_first = short_first
|
|
- self.default_tag = "%default"
|
|
- self.option_strings = {}
|
|
- self._short_opt_fmt = "%s %s"
|
|
- self._long_opt_fmt = "%s=%s"
|
|
-
|
|
- def set_parser(self, parser):
|
|
- self.parser = parser
|
|
-
|
|
- def set_short_opt_delimiter(self, delim):
|
|
- if delim not in ("", " "):
|
|
- raise ValueError(
|
|
- "invalid metavar delimiter for short options: %r" % delim)
|
|
- self._short_opt_fmt = "%s" + delim + "%s"
|
|
-
|
|
- def set_long_opt_delimiter(self, delim):
|
|
- if delim not in ("=", " "):
|
|
- raise ValueError(
|
|
- "invalid metavar delimiter for long options: %r" % delim)
|
|
- self._long_opt_fmt = "%s" + delim + "%s"
|
|
-
|
|
- def indent(self):
|
|
- self.current_indent += self.indent_increment
|
|
- self.level += 1
|
|
-
|
|
- def dedent(self):
|
|
- self.current_indent -= self.indent_increment
|
|
- assert self.current_indent >= 0, "Indent decreased below 0."
|
|
- self.level -= 1
|
|
-
|
|
- def format_usage(self, usage):
|
|
- raise NotImplementedError, "subclasses must implement"
|
|
-
|
|
- def format_heading(self, heading):
|
|
- raise NotImplementedError, "subclasses must implement"
|
|
-
|
|
- def _format_text(self, text):
|
|
- """
|
|
- Format a paragraph of free-form text for inclusion in the
|
|
- help output at the current indentation level.
|
|
- """
|
|
- text_width = self.width - self.current_indent
|
|
- indent = " "*self.current_indent
|
|
- return textwrap.fill(text,
|
|
- text_width,
|
|
- initial_indent=indent,
|
|
- subsequent_indent=indent)
|
|
-
|
|
- def format_description(self, description):
|
|
- if description:
|
|
- return self._format_text(description) + "\n"
|
|
- else:
|
|
- return ""
|
|
-
|
|
- def format_epilog(self, epilog):
|
|
- if epilog:
|
|
- return "\n" + self._format_text(epilog) + "\n"
|
|
- else:
|
|
- return ""
|
|
-
|
|
-
|
|
- def expand_default(self, option):
|
|
- if self.parser is None or not self.default_tag:
|
|
- return option.help
|
|
-
|
|
- default_value = self.parser.defaults.get(option.dest)
|
|
- if default_value is NO_DEFAULT or default_value is None:
|
|
- default_value = self.NO_DEFAULT_VALUE
|
|
-
|
|
- return option.help.replace(self.default_tag, str(default_value))
|
|
-
|
|
- def format_option(self, option):
|
|
- # The help for each option consists of two parts:
|
|
- # * the opt strings and metavars
|
|
- # eg. ("-x", or "-fFILENAME, --file=FILENAME")
|
|
- # * the user-supplied help string
|
|
- # eg. ("turn on expert mode", "read data from FILENAME")
|
|
- #
|
|
- # If possible, we write both of these on the same line:
|
|
- # -x turn on expert mode
|
|
- #
|
|
- # But if the opt string list is too long, we put the help
|
|
- # string on a second line, indented to the same column it would
|
|
- # start in if it fit on the first line.
|
|
- # -fFILENAME, --file=FILENAME
|
|
- # read data from FILENAME
|
|
- result = []
|
|
- opts = self.option_strings[option]
|
|
- opt_width = self.help_position - self.current_indent - 2
|
|
- if len(opts) > opt_width:
|
|
- opts = "%*s%s\n" % (self.current_indent, "", opts)
|
|
- indent_first = self.help_position
|
|
- else: # start help on same line as opts
|
|
- opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts)
|
|
- indent_first = 0
|
|
- result.append(opts)
|
|
- if option.help:
|
|
- help_text = self.expand_default(option)
|
|
- help_lines = textwrap.wrap(help_text, self.help_width)
|
|
- result.append("%*s%s\n" % (indent_first, "", help_lines[0]))
|
|
- result.extend(["%*s%s\n" % (self.help_position, "", line)
|
|
- for line in help_lines[1:]])
|
|
- elif opts[-1] != "\n":
|
|
- result.append("\n")
|
|
- return "".join(result)
|
|
-
|
|
- def store_option_strings(self, parser):
|
|
- self.indent()
|
|
- max_len = 0
|
|
- for opt in parser.option_list:
|
|
- strings = self.format_option_strings(opt)
|
|
- self.option_strings[opt] = strings
|
|
- max_len = max(max_len, len(strings) + self.current_indent)
|
|
- self.indent()
|
|
- for group in parser.option_groups:
|
|
- for opt in group.option_list:
|
|
- strings = self.format_option_strings(opt)
|
|
- self.option_strings[opt] = strings
|
|
- max_len = max(max_len, len(strings) + self.current_indent)
|
|
- self.dedent()
|
|
- self.dedent()
|
|
- self.help_position = min(max_len + 2, self.max_help_position)
|
|
- self.help_width = self.width - self.help_position
|
|
-
|
|
- def format_option_strings(self, option):
|
|
- """Return a comma-separated list of option strings & metavariables."""
|
|
- if option.takes_value():
|
|
- metavar = option.metavar or option.dest.upper()
|
|
- short_opts = [self._short_opt_fmt % (sopt, metavar)
|
|
- for sopt in option._short_opts]
|
|
- long_opts = [self._long_opt_fmt % (lopt, metavar)
|
|
- for lopt in option._long_opts]
|
|
- else:
|
|
- short_opts = option._short_opts
|
|
- long_opts = option._long_opts
|
|
-
|
|
- if self.short_first:
|
|
- opts = short_opts + long_opts
|
|
- else:
|
|
- opts = long_opts + short_opts
|
|
-
|
|
- return ", ".join(opts)
|
|
-
|
|
-class IndentedHelpFormatter (HelpFormatter):
|
|
- """Format help with indented section bodies.
|
|
- """
|
|
-
|
|
- def __init__(self,
|
|
- indent_increment=2,
|
|
- max_help_position=24,
|
|
- width=None,
|
|
- short_first=1):
|
|
- HelpFormatter.__init__(
|
|
- self, indent_increment, max_help_position, width, short_first)
|
|
-
|
|
- def format_usage(self, usage):
|
|
- return _("Usage: %s\n") % usage
|
|
-
|
|
- def format_heading(self, heading):
|
|
- return "%*s%s:\n" % (self.current_indent, "", heading)
|
|
-
|
|
-
|
|
-class TitledHelpFormatter (HelpFormatter):
|
|
- """Format help with underlined section headers.
|
|
- """
|
|
-
|
|
- def __init__(self,
|
|
- indent_increment=0,
|
|
- max_help_position=24,
|
|
- width=None,
|
|
- short_first=0):
|
|
- HelpFormatter.__init__ (
|
|
- self, indent_increment, max_help_position, width, short_first)
|
|
-
|
|
- def format_usage(self, usage):
|
|
- return "%s %s\n" % (self.format_heading(_("Usage")), usage)
|
|
-
|
|
- def format_heading(self, heading):
|
|
- return "%s\n%s\n" % (heading, "=-"[self.level] * len(heading))
|
|
-
|
|
-
|
|
-def _parse_num(val, type):
|
|
- if val[:2].lower() == "0x": # hexadecimal
|
|
- radix = 16
|
|
- elif val[:2].lower() == "0b": # binary
|
|
- radix = 2
|
|
- val = val[2:] or "0" # have to remove "0b" prefix
|
|
- elif val[:1] == "0": # octal
|
|
- radix = 8
|
|
- else: # decimal
|
|
- radix = 10
|
|
-
|
|
- return type(val, radix)
|
|
-
|
|
-def _parse_int(val):
|
|
- return _parse_num(val, int)
|
|
-
|
|
-def _parse_long(val):
|
|
- return _parse_num(val, long)
|
|
-
|
|
-_builtin_cvt = { "int" : (_parse_int, _("integer")),
|
|
- "long" : (_parse_long, _("long integer")),
|
|
- "float" : (float, _("floating-point")),
|
|
- "complex" : (complex, _("complex")) }
|
|
-
|
|
-def check_builtin(option, opt, value):
|
|
- (cvt, what) = _builtin_cvt[option.type]
|
|
- try:
|
|
- return cvt(value)
|
|
- except ValueError:
|
|
- raise OptionValueError(
|
|
- _("option %s: invalid %s value: %r") % (opt, what, value))
|
|
-
|
|
-def check_choice(option, opt, value):
|
|
- if value in option.choices:
|
|
- return value
|
|
- else:
|
|
- choices = ", ".join(map(repr, option.choices))
|
|
- raise OptionValueError(
|
|
- _("option %s: invalid choice: %r (choose from %s)")
|
|
- % (opt, value, choices))
|
|
-
|
|
-# Not supplying a default is different from a default of None,
|
|
-# so we need an explicit "not supplied" value.
|
|
-NO_DEFAULT = ("NO", "DEFAULT")
|
|
-
|
|
-
|
|
-class Option:
|
|
- """
|
|
- Instance attributes:
|
|
- _short_opts : [string]
|
|
- _long_opts : [string]
|
|
-
|
|
- action : string
|
|
- type : string
|
|
- dest : string
|
|
- default : any
|
|
- nargs : int
|
|
- const : any
|
|
- choices : [string]
|
|
- callback : function
|
|
- callback_args : (any*)
|
|
- callback_kwargs : { string : any }
|
|
- help : string
|
|
- metavar : string
|
|
- """
|
|
-
|
|
- # The list of instance attributes that may be set through
|
|
- # keyword args to the constructor.
|
|
- ATTRS = ['action',
|
|
- 'type',
|
|
- 'dest',
|
|
- 'default',
|
|
- 'nargs',
|
|
- 'const',
|
|
- 'choices',
|
|
- 'callback',
|
|
- 'callback_args',
|
|
- 'callback_kwargs',
|
|
- 'help',
|
|
- 'metavar']
|
|
-
|
|
- # The set of actions allowed by option parsers. Explicitly listed
|
|
- # here so the constructor can validate its arguments.
|
|
- ACTIONS = ("store",
|
|
- "store_const",
|
|
- "store_true",
|
|
- "store_false",
|
|
- "append",
|
|
- "append_const",
|
|
- "count",
|
|
- "callback",
|
|
- "help",
|
|
- "version")
|
|
-
|
|
- # The set of actions that involve storing a value somewhere;
|
|
- # also listed just for constructor argument validation. (If
|
|
- # the action is one of these, there must be a destination.)
|
|
- STORE_ACTIONS = ("store",
|
|
- "store_const",
|
|
- "store_true",
|
|
- "store_false",
|
|
- "append",
|
|
- "append_const",
|
|
- "count")
|
|
-
|
|
- # The set of actions for which it makes sense to supply a value
|
|
- # type, ie. which may consume an argument from the command line.
|
|
- TYPED_ACTIONS = ("store",
|
|
- "append",
|
|
- "callback")
|
|
-
|
|
- # The set of actions which *require* a value type, ie. that
|
|
- # always consume an argument from the command line.
|
|
- ALWAYS_TYPED_ACTIONS = ("store",
|
|
- "append")
|
|
-
|
|
- # The set of actions which take a 'const' attribute.
|
|
- CONST_ACTIONS = ("store_const",
|
|
- "append_const")
|
|
-
|
|
- # The set of known types for option parsers. Again, listed here for
|
|
- # constructor argument validation.
|
|
- TYPES = ("string", "int", "long", "float", "complex", "choice")
|
|
-
|
|
- # Dictionary of argument checking functions, which convert and
|
|
- # validate option arguments according to the option type.
|
|
- #
|
|
- # Signature of checking functions is:
|
|
- # check(option : Option, opt : string, value : string) -> any
|
|
- # where
|
|
- # option is the Option instance calling the checker
|
|
- # opt is the actual option seen on the command-line
|
|
- # (eg. "-a", "--file")
|
|
- # value is the option argument seen on the command-line
|
|
- #
|
|
- # The return value should be in the appropriate Python type
|
|
- # for option.type -- eg. an integer if option.type == "int".
|
|
- #
|
|
- # If no checker is defined for a type, arguments will be
|
|
- # unchecked and remain strings.
|
|
- TYPE_CHECKER = { "int" : check_builtin,
|
|
- "long" : check_builtin,
|
|
- "float" : check_builtin,
|
|
- "complex": check_builtin,
|
|
- "choice" : check_choice,
|
|
- }
|
|
-
|
|
-
|
|
- # CHECK_METHODS is a list of unbound method objects; they are called
|
|
- # by the constructor, in order, after all attributes are
|
|
- # initialized. The list is created and filled in later, after all
|
|
- # the methods are actually defined. (I just put it here because I
|
|
- # like to define and document all class attributes in the same
|
|
- # place.) Subclasses that add another _check_*() method should
|
|
- # define their own CHECK_METHODS list that adds their check method
|
|
- # to those from this class.
|
|
- CHECK_METHODS = None
|
|
-
|
|
-
|
|
- # -- Constructor/initialization methods ----------------------------
|
|
-
|
|
- def __init__(self, *opts, **attrs):
|
|
- # Set _short_opts, _long_opts attrs from 'opts' tuple.
|
|
- # Have to be set now, in case no option strings are supplied.
|
|
- self._short_opts = []
|
|
- self._long_opts = []
|
|
- opts = self._check_opt_strings(opts)
|
|
- self._set_opt_strings(opts)
|
|
-
|
|
- # Set all other attrs (action, type, etc.) from 'attrs' dict
|
|
- self._set_attrs(attrs)
|
|
-
|
|
- # Check all the attributes we just set. There are lots of
|
|
- # complicated interdependencies, but luckily they can be farmed
|
|
- # out to the _check_*() methods listed in CHECK_METHODS -- which
|
|
- # could be handy for subclasses! The one thing these all share
|
|
- # is that they raise OptionError if they discover a problem.
|
|
- for checker in self.CHECK_METHODS:
|
|
- checker(self)
|
|
-
|
|
- def _check_opt_strings(self, opts):
|
|
- # Filter out None because early versions of Optik had exactly
|
|
- # one short option and one long option, either of which
|
|
- # could be None.
|
|
- opts = filter(None, opts)
|
|
- if not opts:
|
|
- raise TypeError("at least one option string must be supplied")
|
|
- return opts
|
|
-
|
|
- def _set_opt_strings(self, opts):
|
|
- for opt in opts:
|
|
- if len(opt) < 2:
|
|
- raise OptionError(
|
|
- "invalid option string %r: "
|
|
- "must be at least two characters long" % opt, self)
|
|
- elif len(opt) == 2:
|
|
- if not (opt[0] == "-" and opt[1] != "-"):
|
|
- raise OptionError(
|
|
- "invalid short option string %r: "
|
|
- "must be of the form -x, (x any non-dash char)" % opt,
|
|
- self)
|
|
- self._short_opts.append(opt)
|
|
- else:
|
|
- if not (opt[0:2] == "--" and opt[2] != "-"):
|
|
- raise OptionError(
|
|
- "invalid long option string %r: "
|
|
- "must start with --, followed by non-dash" % opt,
|
|
- self)
|
|
- self._long_opts.append(opt)
|
|
-
|
|
- def _set_attrs(self, attrs):
|
|
- for attr in self.ATTRS:
|
|
- if attrs.has_key(attr):
|
|
- setattr(self, attr, attrs[attr])
|
|
- del attrs[attr]
|
|
- else:
|
|
- if attr == 'default':
|
|
- setattr(self, attr, NO_DEFAULT)
|
|
- else:
|
|
- setattr(self, attr, None)
|
|
- if attrs:
|
|
- attrs = attrs.keys()
|
|
- attrs.sort()
|
|
- raise OptionError(
|
|
- "invalid keyword arguments: %s" % ", ".join(attrs),
|
|
- self)
|
|
-
|
|
-
|
|
- # -- Constructor validation methods --------------------------------
|
|
-
|
|
- def _check_action(self):
|
|
- if self.action is None:
|
|
- self.action = "store"
|
|
- elif self.action not in self.ACTIONS:
|
|
- raise OptionError("invalid action: %r" % self.action, self)
|
|
-
|
|
- def _check_type(self):
|
|
- if self.type is None:
|
|
- if self.action in self.ALWAYS_TYPED_ACTIONS:
|
|
- if self.choices is not None:
|
|
- # The "choices" attribute implies "choice" type.
|
|
- self.type = "choice"
|
|
- else:
|
|
- # No type given? "string" is the most sensible default.
|
|
- self.type = "string"
|
|
- else:
|
|
- # Allow type objects or builtin type conversion functions
|
|
- # (int, str, etc.) as an alternative to their names. (The
|
|
- # complicated check of __builtin__ is only necessary for
|
|
- # Python 2.1 and earlier, and is short-circuited by the
|
|
- # first check on modern Pythons.)
|
|
- import __builtin__
|
|
- if ( type(self.type) is types.TypeType or
|
|
- (hasattr(self.type, "__name__") and
|
|
- getattr(__builtin__, self.type.__name__, None) is self.type) ):
|
|
- self.type = self.type.__name__
|
|
-
|
|
- if self.type == "str":
|
|
- self.type = "string"
|
|
-
|
|
- if self.type not in self.TYPES:
|
|
- raise OptionError("invalid option type: %r" % self.type, self)
|
|
- if self.action not in self.TYPED_ACTIONS:
|
|
- raise OptionError(
|
|
- "must not supply a type for action %r" % self.action, self)
|
|
-
|
|
- def _check_choice(self):
|
|
- if self.type == "choice":
|
|
- if self.choices is None:
|
|
- raise OptionError(
|
|
- "must supply a list of choices for type 'choice'", self)
|
|
- elif type(self.choices) not in (types.TupleType, types.ListType):
|
|
- raise OptionError(
|
|
- "choices must be a list of strings ('%s' supplied)"
|
|
- % str(type(self.choices)).split("'")[1], self)
|
|
- elif self.choices is not None:
|
|
- raise OptionError(
|
|
- "must not supply choices for type %r" % self.type, self)
|
|
-
|
|
- def _check_dest(self):
|
|
- # No destination given, and we need one for this action. The
|
|
- # self.type check is for callbacks that take a value.
|
|
- takes_value = (self.action in self.STORE_ACTIONS or
|
|
- self.type is not None)
|
|
- if self.dest is None and takes_value:
|
|
-
|
|
- # Glean a destination from the first long option string,
|
|
- # or from the first short option string if no long options.
|
|
- if self._long_opts:
|
|
- # eg. "--foo-bar" -> "foo_bar"
|
|
- self.dest = self._long_opts[0][2:].replace('-', '_')
|
|
- else:
|
|
- self.dest = self._short_opts[0][1]
|
|
-
|
|
- def _check_const(self):
|
|
- if self.action not in self.CONST_ACTIONS and self.const is not None:
|
|
- raise OptionError(
|
|
- "'const' must not be supplied for action %r" % self.action,
|
|
- self)
|
|
-
|
|
- def _check_nargs(self):
|
|
- if self.action in self.TYPED_ACTIONS:
|
|
- if self.nargs is None:
|
|
- self.nargs = 1
|
|
- elif self.nargs is not None:
|
|
- raise OptionError(
|
|
- "'nargs' must not be supplied for action %r" % self.action,
|
|
- self)
|
|
-
|
|
- def _check_callback(self):
|
|
- if self.action == "callback":
|
|
- if not callable(self.callback):
|
|
- raise OptionError(
|
|
- "callback not callable: %r" % self.callback, self)
|
|
- if (self.callback_args is not None and
|
|
- type(self.callback_args) is not types.TupleType):
|
|
- raise OptionError(
|
|
- "callback_args, if supplied, must be a tuple: not %r"
|
|
- % self.callback_args, self)
|
|
- if (self.callback_kwargs is not None and
|
|
- type(self.callback_kwargs) is not types.DictType):
|
|
- raise OptionError(
|
|
- "callback_kwargs, if supplied, must be a dict: not %r"
|
|
- % self.callback_kwargs, self)
|
|
- else:
|
|
- if self.callback is not None:
|
|
- raise OptionError(
|
|
- "callback supplied (%r) for non-callback option"
|
|
- % self.callback, self)
|
|
- if self.callback_args is not None:
|
|
- raise OptionError(
|
|
- "callback_args supplied for non-callback option", self)
|
|
- if self.callback_kwargs is not None:
|
|
- raise OptionError(
|
|
- "callback_kwargs supplied for non-callback option", self)
|
|
-
|
|
-
|
|
- CHECK_METHODS = [_check_action,
|
|
- _check_type,
|
|
- _check_choice,
|
|
- _check_dest,
|
|
- _check_const,
|
|
- _check_nargs,
|
|
- _check_callback]
|
|
-
|
|
-
|
|
- # -- Miscellaneous methods -----------------------------------------
|
|
-
|
|
- def __str__(self):
|
|
- return "/".join(self._short_opts + self._long_opts)
|
|
-
|
|
- __repr__ = _repr
|
|
-
|
|
- def takes_value(self):
|
|
- return self.type is not None
|
|
-
|
|
- def get_opt_string(self):
|
|
- if self._long_opts:
|
|
- return self._long_opts[0]
|
|
- else:
|
|
- return self._short_opts[0]
|
|
-
|
|
-
|
|
- # -- Processing methods --------------------------------------------
|
|
-
|
|
- def check_value(self, opt, value):
|
|
- checker = self.TYPE_CHECKER.get(self.type)
|
|
- if checker is None:
|
|
- return value
|
|
- else:
|
|
- return checker(self, opt, value)
|
|
-
|
|
- def convert_value(self, opt, value):
|
|
- if value is not None:
|
|
- if self.nargs == 1:
|
|
- return self.check_value(opt, value)
|
|
- else:
|
|
- return tuple([self.check_value(opt, v) for v in value])
|
|
-
|
|
- def process(self, opt, value, values, parser):
|
|
-
|
|
- # First, convert the value(s) to the right type. Howl if any
|
|
- # value(s) are bogus.
|
|
- value = self.convert_value(opt, value)
|
|
-
|
|
- # And then take whatever action is expected of us.
|
|
- # This is a separate method to make life easier for
|
|
- # subclasses to add new actions.
|
|
- return self.take_action(
|
|
- self.action, self.dest, opt, value, values, parser)
|
|
-
|
|
- def take_action(self, action, dest, opt, value, values, parser):
|
|
- if action == "store":
|
|
- setattr(values, dest, value)
|
|
- elif action == "store_const":
|
|
- setattr(values, dest, self.const)
|
|
- elif action == "store_true":
|
|
- setattr(values, dest, True)
|
|
- elif action == "store_false":
|
|
- setattr(values, dest, False)
|
|
- elif action == "append":
|
|
- values.ensure_value(dest, []).append(value)
|
|
- elif action == "append_const":
|
|
- values.ensure_value(dest, []).append(self.const)
|
|
- elif action == "count":
|
|
- setattr(values, dest, values.ensure_value(dest, 0) + 1)
|
|
- elif action == "callback":
|
|
- args = self.callback_args or ()
|
|
- kwargs = self.callback_kwargs or {}
|
|
- self.callback(self, opt, value, parser, *args, **kwargs)
|
|
- elif action == "help":
|
|
- parser.print_help()
|
|
- parser.exit()
|
|
- elif action == "version":
|
|
- parser.print_version()
|
|
- parser.exit()
|
|
- else:
|
|
- raise RuntimeError, "unknown action %r" % self.action
|
|
-
|
|
- return 1
|
|
-
|
|
-# class Option
|
|
-
|
|
-
|
|
-SUPPRESS_HELP = "SUPPRESS"+"HELP"
|
|
-SUPPRESS_USAGE = "SUPPRESS"+"USAGE"
|
|
-
|
|
-# For compatibility with Python 2.2
|
|
-try:
|
|
- True, False
|
|
-except NameError:
|
|
- (True, False) = (1, 0)
|
|
-
|
|
-def isbasestring(x):
|
|
- return isinstance(x, types.StringType) or isinstance(x, types.UnicodeType)
|
|
-
|
|
-class Values:
|
|
-
|
|
- def __init__(self, defaults=None):
|
|
- if defaults:
|
|
- for (attr, val) in defaults.items():
|
|
- setattr(self, attr, val)
|
|
-
|
|
- def __str__(self):
|
|
- return str(self.__dict__)
|
|
-
|
|
- __repr__ = _repr
|
|
-
|
|
- def __cmp__(self, other):
|
|
- if isinstance(other, Values):
|
|
- return cmp(self.__dict__, other.__dict__)
|
|
- elif isinstance(other, types.DictType):
|
|
- return cmp(self.__dict__, other)
|
|
- else:
|
|
- return -1
|
|
-
|
|
- def _update_careful(self, dict):
|
|
- """
|
|
- Update the option values from an arbitrary dictionary, but only
|
|
- use keys from dict that already have a corresponding attribute
|
|
- in self. Any keys in dict without a corresponding attribute
|
|
- are silently ignored.
|
|
- """
|
|
- for attr in dir(self):
|
|
- if dict.has_key(attr):
|
|
- dval = dict[attr]
|
|
- if dval is not None:
|
|
- setattr(self, attr, dval)
|
|
-
|
|
- def _update_loose(self, dict):
|
|
- """
|
|
- Update the option values from an arbitrary dictionary,
|
|
- using all keys from the dictionary regardless of whether
|
|
- they have a corresponding attribute in self or not.
|
|
- """
|
|
- self.__dict__.update(dict)
|
|
-
|
|
- def _update(self, dict, mode):
|
|
- if mode == "careful":
|
|
- self._update_careful(dict)
|
|
- elif mode == "loose":
|
|
- self._update_loose(dict)
|
|
- else:
|
|
- raise ValueError, "invalid update mode: %r" % mode
|
|
-
|
|
- def read_module(self, modname, mode="careful"):
|
|
- __import__(modname)
|
|
- mod = sys.modules[modname]
|
|
- self._update(vars(mod), mode)
|
|
-
|
|
- def read_file(self, filename, mode="careful"):
|
|
- vars = {}
|
|
- execfile(filename, vars)
|
|
- self._update(vars, mode)
|
|
-
|
|
- def ensure_value(self, attr, value):
|
|
- if not hasattr(self, attr) or getattr(self, attr) is None:
|
|
- setattr(self, attr, value)
|
|
- return getattr(self, attr)
|
|
-
|
|
-
|
|
-class OptionContainer:
|
|
-
|
|
- """
|
|
- Abstract base class.
|
|
-
|
|
- Class attributes:
|
|
- standard_option_list : [Option]
|
|
- list of standard options that will be accepted by all instances
|
|
- of this parser class (intended to be overridden by subclasses).
|
|
-
|
|
- Instance attributes:
|
|
- option_list : [Option]
|
|
- the list of Option objects contained by this OptionContainer
|
|
- _short_opt : { string : Option }
|
|
- dictionary mapping short option strings, eg. "-f" or "-X",
|
|
- to the Option instances that implement them. If an Option
|
|
- has multiple short option strings, it will appears in this
|
|
- dictionary multiple times. [1]
|
|
- _long_opt : { string : Option }
|
|
- dictionary mapping long option strings, eg. "--file" or
|
|
- "--exclude", to the Option instances that implement them.
|
|
- Again, a given Option can occur multiple times in this
|
|
- dictionary. [1]
|
|
- defaults : { string : any }
|
|
- dictionary mapping option destination names to default
|
|
- values for each destination [1]
|
|
-
|
|
- [1] These mappings are common to (shared by) all components of the
|
|
- controlling OptionParser, where they are initially created.
|
|
-
|
|
- """
|
|
-
|
|
- def __init__(self, option_class, conflict_handler, description):
|
|
- # Initialize the option list and related data structures.
|
|
- # This method must be provided by subclasses, and it must
|
|
- # initialize at least the following instance attributes:
|
|
- # option_list, _short_opt, _long_opt, defaults.
|
|
- self._create_option_list()
|
|
-
|
|
- self.option_class = option_class
|
|
- self.set_conflict_handler(conflict_handler)
|
|
- self.set_description(description)
|
|
-
|
|
- def _create_option_mappings(self):
|
|
- # For use by OptionParser constructor -- create the master
|
|
- # option mappings used by this OptionParser and all
|
|
- # OptionGroups that it owns.
|
|
- self._short_opt = {} # single letter -> Option instance
|
|
- self._long_opt = {} # long option -> Option instance
|
|
- self.defaults = {} # maps option dest -> default value
|
|
-
|
|
-
|
|
- def _share_option_mappings(self, parser):
|
|
- # For use by OptionGroup constructor -- use shared option
|
|
- # mappings from the OptionParser that owns this OptionGroup.
|
|
- self._short_opt = parser._short_opt
|
|
- self._long_opt = parser._long_opt
|
|
- self.defaults = parser.defaults
|
|
-
|
|
- def set_conflict_handler(self, handler):
|
|
- if handler not in ("error", "resolve"):
|
|
- raise ValueError, "invalid conflict_resolution value %r" % handler
|
|
- self.conflict_handler = handler
|
|
-
|
|
- def set_description(self, description):
|
|
- self.description = description
|
|
-
|
|
- def get_description(self):
|
|
- return self.description
|
|
-
|
|
-
|
|
- def destroy(self):
|
|
- """see OptionParser.destroy()."""
|
|
- del self._short_opt
|
|
- del self._long_opt
|
|
- del self.defaults
|
|
-
|
|
-
|
|
- # -- Option-adding methods -----------------------------------------
|
|
-
|
|
- def _check_conflict(self, option):
|
|
- conflict_opts = []
|
|
- for opt in option._short_opts:
|
|
- if self._short_opt.has_key(opt):
|
|
- conflict_opts.append((opt, self._short_opt[opt]))
|
|
- for opt in option._long_opts:
|
|
- if self._long_opt.has_key(opt):
|
|
- conflict_opts.append((opt, self._long_opt[opt]))
|
|
-
|
|
- if conflict_opts:
|
|
- handler = self.conflict_handler
|
|
- if handler == "error":
|
|
- raise OptionConflictError(
|
|
- "conflicting option string(s): %s"
|
|
- % ", ".join([co[0] for co in conflict_opts]),
|
|
- option)
|
|
- elif handler == "resolve":
|
|
- for (opt, c_option) in conflict_opts:
|
|
- if opt.startswith("--"):
|
|
- c_option._long_opts.remove(opt)
|
|
- del self._long_opt[opt]
|
|
- else:
|
|
- c_option._short_opts.remove(opt)
|
|
- del self._short_opt[opt]
|
|
- if not (c_option._short_opts or c_option._long_opts):
|
|
- c_option.container.option_list.remove(c_option)
|
|
-
|
|
- def add_option(self, *args, **kwargs):
|
|
- """add_option(Option)
|
|
- add_option(opt_str, ..., kwarg=val, ...)
|
|
- """
|
|
- if type(args[0]) is types.StringType:
|
|
- option = self.option_class(*args, **kwargs)
|
|
- elif len(args) == 1 and not kwargs:
|
|
- option = args[0]
|
|
- if not isinstance(option, Option):
|
|
- raise TypeError, "not an Option instance: %r" % option
|
|
- else:
|
|
- raise TypeError, "invalid arguments"
|
|
-
|
|
- self._check_conflict(option)
|
|
-
|
|
- self.option_list.append(option)
|
|
- option.container = self
|
|
- for opt in option._short_opts:
|
|
- self._short_opt[opt] = option
|
|
- for opt in option._long_opts:
|
|
- self._long_opt[opt] = option
|
|
-
|
|
- if option.dest is not None: # option has a dest, we need a default
|
|
- if option.default is not NO_DEFAULT:
|
|
- self.defaults[option.dest] = option.default
|
|
- elif not self.defaults.has_key(option.dest):
|
|
- self.defaults[option.dest] = None
|
|
-
|
|
- return option
|
|
-
|
|
- def add_options(self, option_list):
|
|
- for option in option_list:
|
|
- self.add_option(option)
|
|
-
|
|
- # -- Option query/removal methods ----------------------------------
|
|
-
|
|
- def get_option(self, opt_str):
|
|
- return (self._short_opt.get(opt_str) or
|
|
- self._long_opt.get(opt_str))
|
|
-
|
|
- def has_option(self, opt_str):
|
|
- return (self._short_opt.has_key(opt_str) or
|
|
- self._long_opt.has_key(opt_str))
|
|
-
|
|
- def remove_option(self, opt_str):
|
|
- option = self._short_opt.get(opt_str)
|
|
- if option is None:
|
|
- option = self._long_opt.get(opt_str)
|
|
- if option is None:
|
|
- raise ValueError("no such option %r" % opt_str)
|
|
-
|
|
- for opt in option._short_opts:
|
|
- del self._short_opt[opt]
|
|
- for opt in option._long_opts:
|
|
- del self._long_opt[opt]
|
|
- option.container.option_list.remove(option)
|
|
-
|
|
-
|
|
- # -- Help-formatting methods ---------------------------------------
|
|
-
|
|
- def format_option_help(self, formatter):
|
|
- if not self.option_list:
|
|
- return ""
|
|
- result = []
|
|
- for option in self.option_list:
|
|
- if not option.help is SUPPRESS_HELP:
|
|
- result.append(formatter.format_option(option))
|
|
- return "".join(result)
|
|
-
|
|
- def format_description(self, formatter):
|
|
- return formatter.format_description(self.get_description())
|
|
-
|
|
- def format_help(self, formatter):
|
|
- result = []
|
|
- if self.description:
|
|
- result.append(self.format_description(formatter))
|
|
- if self.option_list:
|
|
- result.append(self.format_option_help(formatter))
|
|
- return "\n".join(result)
|
|
-
|
|
-
|
|
-class OptionGroup (OptionContainer):
|
|
-
|
|
- def __init__(self, parser, title, description=None):
|
|
- self.parser = parser
|
|
- OptionContainer.__init__(
|
|
- self, parser.option_class, parser.conflict_handler, description)
|
|
- self.title = title
|
|
-
|
|
- def _create_option_list(self):
|
|
- self.option_list = []
|
|
- self._share_option_mappings(self.parser)
|
|
-
|
|
- def set_title(self, title):
|
|
- self.title = title
|
|
-
|
|
- def destroy(self):
|
|
- """see OptionParser.destroy()."""
|
|
- OptionContainer.destroy(self)
|
|
- del self.option_list
|
|
-
|
|
- # -- Help-formatting methods ---------------------------------------
|
|
-
|
|
- def format_help(self, formatter):
|
|
- result = formatter.format_heading(self.title)
|
|
- formatter.indent()
|
|
- result += OptionContainer.format_help(self, formatter)
|
|
- formatter.dedent()
|
|
- return result
|
|
-
|
|
-
|
|
-class OptionParser (OptionContainer):
|
|
-
|
|
- """
|
|
- Class attributes:
|
|
- standard_option_list : [Option]
|
|
- list of standard options that will be accepted by all instances
|
|
- of this parser class (intended to be overridden by subclasses).
|
|
-
|
|
- Instance attributes:
|
|
- usage : string
|
|
- a usage string for your program. Before it is displayed
|
|
- to the user, "%prog" will be expanded to the name of
|
|
- your program (self.prog or os.path.basename(sys.argv[0])).
|
|
- prog : string
|
|
- the name of the current program (to override
|
|
- os.path.basename(sys.argv[0])).
|
|
- epilog : string
|
|
- paragraph of help text to print after option help
|
|
-
|
|
- option_groups : [OptionGroup]
|
|
- list of option groups in this parser (option groups are
|
|
- irrelevant for parsing the command-line, but very useful
|
|
- for generating help)
|
|
-
|
|
- allow_interspersed_args : bool = true
|
|
- if true, positional arguments may be interspersed with options.
|
|
- Assuming -a and -b each take a single argument, the command-line
|
|
- -ablah foo bar -bboo baz
|
|
- will be interpreted the same as
|
|
- -ablah -bboo -- foo bar baz
|
|
- If this flag were false, that command line would be interpreted as
|
|
- -ablah -- foo bar -bboo baz
|
|
- -- ie. we stop processing options as soon as we see the first
|
|
- non-option argument. (This is the tradition followed by
|
|
- Python's getopt module, Perl's Getopt::Std, and other argument-
|
|
- parsing libraries, but it is generally annoying to users.)
|
|
-
|
|
- process_default_values : bool = true
|
|
- if true, option default values are processed similarly to option
|
|
- values from the command line: that is, they are passed to the
|
|
- type-checking function for the option's type (as long as the
|
|
- default value is a string). (This really only matters if you
|
|
- have defined custom types; see SF bug #955889.) Set it to false
|
|
- to restore the behaviour of Optik 1.4.1 and earlier.
|
|
-
|
|
- rargs : [string]
|
|
- the argument list currently being parsed. Only set when
|
|
- parse_args() is active, and continually trimmed down as
|
|
- we consume arguments. Mainly there for the benefit of
|
|
- callback options.
|
|
- largs : [string]
|
|
- the list of leftover arguments that we have skipped while
|
|
- parsing options. If allow_interspersed_args is false, this
|
|
- list is always empty.
|
|
- values : Values
|
|
- the set of option values currently being accumulated. Only
|
|
- set when parse_args() is active. Also mainly for callbacks.
|
|
-
|
|
- Because of the 'rargs', 'largs', and 'values' attributes,
|
|
- OptionParser is not thread-safe. If, for some perverse reason, you
|
|
- need to parse command-line arguments simultaneously in different
|
|
- threads, use different OptionParser instances.
|
|
-
|
|
- """
|
|
-
|
|
- standard_option_list = []
|
|
-
|
|
- def __init__(self,
|
|
- usage=None,
|
|
- option_list=None,
|
|
- option_class=Option,
|
|
- version=None,
|
|
- conflict_handler="error",
|
|
- description=None,
|
|
- formatter=None,
|
|
- add_help_option=True,
|
|
- prog=None,
|
|
- epilog=None):
|
|
- OptionContainer.__init__(
|
|
- self, option_class, conflict_handler, description)
|
|
- self.set_usage(usage)
|
|
- self.prog = prog
|
|
- self.version = version
|
|
- self.allow_interspersed_args = True
|
|
- self.process_default_values = True
|
|
- if formatter is None:
|
|
- formatter = IndentedHelpFormatter()
|
|
- self.formatter = formatter
|
|
- self.formatter.set_parser(self)
|
|
- self.epilog = epilog
|
|
-
|
|
- # Populate the option list; initial sources are the
|
|
- # standard_option_list class attribute, the 'option_list'
|
|
- # argument, and (if applicable) the _add_version_option() and
|
|
- # _add_help_option() methods.
|
|
- self._populate_option_list(option_list,
|
|
- add_help=add_help_option)
|
|
-
|
|
- self._init_parsing_state()
|
|
-
|
|
-
|
|
- def destroy(self):
|
|
- """
|
|
- Declare that you are done with this OptionParser. This cleans up
|
|
- reference cycles so the OptionParser (and all objects referenced by
|
|
- it) can be garbage-collected promptly. After calling destroy(), the
|
|
- OptionParser is unusable.
|
|
- """
|
|
- OptionContainer.destroy(self)
|
|
- for group in self.option_groups:
|
|
- group.destroy()
|
|
- del self.option_list
|
|
- del self.option_groups
|
|
- del self.formatter
|
|
-
|
|
-
|
|
- # -- Private methods -----------------------------------------------
|
|
- # (used by our or OptionContainer's constructor)
|
|
-
|
|
- def _create_option_list(self):
|
|
- self.option_list = []
|
|
- self.option_groups = []
|
|
- self._create_option_mappings()
|
|
-
|
|
- def _add_help_option(self):
|
|
- self.add_option("-h", "--help",
|
|
- action="help",
|
|
- help=_("show this help message and exit"))
|
|
-
|
|
- def _add_version_option(self):
|
|
- self.add_option("--version",
|
|
- action="version",
|
|
- help=_("show program's version number and exit"))
|
|
-
|
|
- def _populate_option_list(self, option_list, add_help=True):
|
|
- if self.standard_option_list:
|
|
- self.add_options(self.standard_option_list)
|
|
- if option_list:
|
|
- self.add_options(option_list)
|
|
- if self.version:
|
|
- self._add_version_option()
|
|
- if add_help:
|
|
- self._add_help_option()
|
|
-
|
|
- def _init_parsing_state(self):
|
|
- # These are set in parse_args() for the convenience of callbacks.
|
|
- self.rargs = None
|
|
- self.largs = None
|
|
- self.values = None
|
|
-
|
|
-
|
|
- # -- Simple modifier methods ---------------------------------------
|
|
-
|
|
- def set_usage(self, usage):
|
|
- if usage is None:
|
|
- self.usage = _("%prog [options]")
|
|
- elif usage is SUPPRESS_USAGE:
|
|
- self.usage = None
|
|
- # For backwards compatibility with Optik 1.3 and earlier.
|
|
- elif usage.lower().startswith("usage: "):
|
|
- self.usage = usage[7:]
|
|
- else:
|
|
- self.usage = usage
|
|
-
|
|
- def enable_interspersed_args(self):
|
|
- self.allow_interspersed_args = True
|
|
-
|
|
- def disable_interspersed_args(self):
|
|
- self.allow_interspersed_args = False
|
|
-
|
|
- def set_process_default_values(self, process):
|
|
- self.process_default_values = process
|
|
-
|
|
- def set_default(self, dest, value):
|
|
- self.defaults[dest] = value
|
|
-
|
|
- def set_defaults(self, **kwargs):
|
|
- self.defaults.update(kwargs)
|
|
-
|
|
- def _get_all_options(self):
|
|
- options = self.option_list[:]
|
|
- for group in self.option_groups:
|
|
- options.extend(group.option_list)
|
|
- return options
|
|
-
|
|
- def get_default_values(self):
|
|
- if not self.process_default_values:
|
|
- # Old, pre-Optik 1.5 behaviour.
|
|
- return Values(self.defaults)
|
|
-
|
|
- defaults = self.defaults.copy()
|
|
- for option in self._get_all_options():
|
|
- default = defaults.get(option.dest)
|
|
- if isbasestring(default):
|
|
- opt_str = option.get_opt_string()
|
|
- defaults[option.dest] = option.check_value(opt_str, default)
|
|
-
|
|
- return Values(defaults)
|
|
-
|
|
-
|
|
- # -- OptionGroup methods -------------------------------------------
|
|
-
|
|
- def add_option_group(self, *args, **kwargs):
|
|
- # XXX lots of overlap with OptionContainer.add_option()
|
|
- if type(args[0]) is types.StringType:
|
|
- group = OptionGroup(self, *args, **kwargs)
|
|
- elif len(args) == 1 and not kwargs:
|
|
- group = args[0]
|
|
- if not isinstance(group, OptionGroup):
|
|
- raise TypeError, "not an OptionGroup instance: %r" % group
|
|
- if group.parser is not self:
|
|
- raise ValueError, "invalid OptionGroup (wrong parser)"
|
|
- else:
|
|
- raise TypeError, "invalid arguments"
|
|
-
|
|
- self.option_groups.append(group)
|
|
- return group
|
|
-
|
|
- def get_option_group(self, opt_str):
|
|
- option = (self._short_opt.get(opt_str) or
|
|
- self._long_opt.get(opt_str))
|
|
- if option and option.container is not self:
|
|
- return option.container
|
|
- return None
|
|
-
|
|
-
|
|
- # -- Option-parsing methods ----------------------------------------
|
|
-
|
|
- def _get_args(self, args):
|
|
- if args is None:
|
|
- return sys.argv[1:]
|
|
- else:
|
|
- return args[:] # don't modify caller's list
|
|
-
|
|
- def parse_args(self, args=None, values=None):
|
|
- """
|
|
- parse_args(args : [string] = sys.argv[1:],
|
|
- values : Values = None)
|
|
- -> (values : Values, args : [string])
|
|
-
|
|
- Parse the command-line options found in 'args' (default:
|
|
- sys.argv[1:]). Any errors result in a call to 'error()', which
|
|
- by default prints the usage message to stderr and calls
|
|
- sys.exit() with an error message. On success returns a pair
|
|
- (values, args) where 'values' is an Values instance (with all
|
|
- your option values) and 'args' is the list of arguments left
|
|
- over after parsing options.
|
|
- """
|
|
- rargs = self._get_args(args)
|
|
- if values is None:
|
|
- values = self.get_default_values()
|
|
-
|
|
- # Store the halves of the argument list as attributes for the
|
|
- # convenience of callbacks:
|
|
- # rargs
|
|
- # the rest of the command-line (the "r" stands for
|
|
- # "remaining" or "right-hand")
|
|
- # largs
|
|
- # the leftover arguments -- ie. what's left after removing
|
|
- # options and their arguments (the "l" stands for "leftover"
|
|
- # or "left-hand")
|
|
- self.rargs = rargs
|
|
- self.largs = largs = []
|
|
- self.values = values
|
|
-
|
|
- try:
|
|
- stop = self._process_args(largs, rargs, values)
|
|
- except (BadOptionError, OptionValueError), err:
|
|
- self.error(str(err))
|
|
-
|
|
- args = largs + rargs
|
|
- return self.check_values(values, args)
|
|
-
|
|
- def check_values(self, values, args):
|
|
- """
|
|
- check_values(values : Values, args : [string])
|
|
- -> (values : Values, args : [string])
|
|
-
|
|
- Check that the supplied option values and leftover arguments are
|
|
- valid. Returns the option values and leftover arguments
|
|
- (possibly adjusted, possibly completely new -- whatever you
|
|
- like). Default implementation just returns the passed-in
|
|
- values; subclasses may override as desired.
|
|
- """
|
|
- return (values, args)
|
|
-
|
|
- def _process_args(self, largs, rargs, values):
|
|
- """_process_args(largs : [string],
|
|
- rargs : [string],
|
|
- values : Values)
|
|
-
|
|
- Process command-line arguments and populate 'values', consuming
|
|
- options and arguments from 'rargs'. If 'allow_interspersed_args' is
|
|
- false, stop at the first non-option argument. If true, accumulate any
|
|
- interspersed non-option arguments in 'largs'.
|
|
- """
|
|
- while rargs:
|
|
- arg = rargs[0]
|
|
- # We handle bare "--" explicitly, and bare "-" is handled by the
|
|
- # standard arg handler since the short arg case ensures that the
|
|
- # len of the opt string is greater than 1.
|
|
- if arg == "--":
|
|
- del rargs[0]
|
|
- return
|
|
- elif arg[0:2] == "--":
|
|
- # process a single long option (possibly with value(s))
|
|
- self._process_long_opt(rargs, values)
|
|
- elif arg[:1] == "-" and len(arg) > 1:
|
|
- # process a cluster of short options (possibly with
|
|
- # value(s) for the last one only)
|
|
- self._process_short_opts(rargs, values)
|
|
- elif self.allow_interspersed_args:
|
|
- largs.append(arg)
|
|
- del rargs[0]
|
|
- else:
|
|
- return # stop now, leave this arg in rargs
|
|
-
|
|
- # Say this is the original argument list:
|
|
- # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)]
|
|
- # ^
|
|
- # (we are about to process arg(i)).
|
|
- #
|
|
- # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of
|
|
- # [arg0, ..., arg(i-1)] (any options and their arguments will have
|
|
- # been removed from largs).
|
|
- #
|
|
- # The while loop will usually consume 1 or more arguments per pass.
|
|
- # If it consumes 1 (eg. arg is an option that takes no arguments),
|
|
- # then after _process_arg() is done the situation is:
|
|
- #
|
|
- # largs = subset of [arg0, ..., arg(i)]
|
|
- # rargs = [arg(i+1), ..., arg(N-1)]
|
|
- #
|
|
- # If allow_interspersed_args is false, largs will always be
|
|
- # *empty* -- still a subset of [arg0, ..., arg(i-1)], but
|
|
- # not a very interesting subset!
|
|
-
|
|
- def _match_long_opt(self, opt):
|
|
- """_match_long_opt(opt : string) -> string
|
|
-
|
|
- Determine which long option string 'opt' matches, ie. which one
|
|
- it is an unambiguous abbrevation for. Raises BadOptionError if
|
|
- 'opt' doesn't unambiguously match any long option string.
|
|
- """
|
|
- return _match_abbrev(opt, self._long_opt)
|
|
-
|
|
- def _process_long_opt(self, rargs, values):
|
|
- arg = rargs.pop(0)
|
|
-
|
|
- # Value explicitly attached to arg? Pretend it's the next
|
|
- # argument.
|
|
- if "=" in arg:
|
|
- (opt, next_arg) = arg.split("=", 1)
|
|
- rargs.insert(0, next_arg)
|
|
- had_explicit_value = True
|
|
- else:
|
|
- opt = arg
|
|
- had_explicit_value = False
|
|
-
|
|
- opt = self._match_long_opt(opt)
|
|
- option = self._long_opt[opt]
|
|
- if option.takes_value():
|
|
- nargs = option.nargs
|
|
- if len(rargs) < nargs:
|
|
- if nargs == 1:
|
|
- self.error(_("%s option requires an argument") % opt)
|
|
- else:
|
|
- self.error(_("%s option requires %d arguments")
|
|
- % (opt, nargs))
|
|
- elif nargs == 1:
|
|
- value = rargs.pop(0)
|
|
- else:
|
|
- value = tuple(rargs[0:nargs])
|
|
- del rargs[0:nargs]
|
|
-
|
|
- elif had_explicit_value:
|
|
- self.error(_("%s option does not take a value") % opt)
|
|
-
|
|
- else:
|
|
- value = None
|
|
-
|
|
- option.process(opt, value, values, self)
|
|
-
|
|
- def _process_short_opts(self, rargs, values):
|
|
- arg = rargs.pop(0)
|
|
- stop = False
|
|
- i = 1
|
|
- for ch in arg[1:]:
|
|
- opt = "-" + ch
|
|
- option = self._short_opt.get(opt)
|
|
- i += 1 # we have consumed a character
|
|
-
|
|
- if not option:
|
|
- raise BadOptionError(opt)
|
|
- if option.takes_value():
|
|
- # Any characters left in arg? Pretend they're the
|
|
- # next arg, and stop consuming characters of arg.
|
|
- if i < len(arg):
|
|
- rargs.insert(0, arg[i:])
|
|
- stop = True
|
|
-
|
|
- nargs = option.nargs
|
|
- if len(rargs) < nargs:
|
|
- if nargs == 1:
|
|
- self.error(_("%s option requires an argument") % opt)
|
|
- else:
|
|
- self.error(_("%s option requires %d arguments")
|
|
- % (opt, nargs))
|
|
- elif nargs == 1:
|
|
- value = rargs.pop(0)
|
|
- else:
|
|
- value = tuple(rargs[0:nargs])
|
|
- del rargs[0:nargs]
|
|
-
|
|
- else: # option doesn't take a value
|
|
- value = None
|
|
-
|
|
- option.process(opt, value, values, self)
|
|
-
|
|
- if stop:
|
|
- break
|
|
-
|
|
-
|
|
- # -- Feedback methods ----------------------------------------------
|
|
-
|
|
- def get_prog_name(self):
|
|
- if self.prog is None:
|
|
- return os.path.basename(sys.argv[0])
|
|
- else:
|
|
- return self.prog
|
|
-
|
|
- def expand_prog_name(self, s):
|
|
- return s.replace("%prog", self.get_prog_name())
|
|
-
|
|
- def get_description(self):
|
|
- return self.expand_prog_name(self.description)
|
|
-
|
|
- def exit(self, status=0, msg=None):
|
|
- if msg:
|
|
- sys.stderr.write(msg)
|
|
- sys.exit(status)
|
|
-
|
|
- def error(self, msg):
|
|
- """error(msg : string)
|
|
-
|
|
- Print a usage message incorporating 'msg' to stderr and exit.
|
|
- If you override this in a subclass, it should not return -- it
|
|
- should either exit or raise an exception.
|
|
- """
|
|
- self.print_usage(sys.stderr)
|
|
- self.exit(2, "%s: error: %s\n" % (self.get_prog_name(), msg))
|
|
-
|
|
- def get_usage(self):
|
|
- if self.usage:
|
|
- return self.formatter.format_usage(
|
|
- self.expand_prog_name(self.usage))
|
|
- else:
|
|
- return ""
|
|
-
|
|
- def print_usage(self, file=None):
|
|
- """print_usage(file : file = stdout)
|
|
-
|
|
- Print the usage message for the current program (self.usage) to
|
|
- 'file' (default stdout). Any occurence of the string "%prog" in
|
|
- self.usage is replaced with the name of the current program
|
|
- (basename of sys.argv[0]). Does nothing if self.usage is empty
|
|
- or not defined.
|
|
- """
|
|
- if self.usage:
|
|
- print >>file, self.get_usage()
|
|
-
|
|
- def get_version(self):
|
|
- if self.version:
|
|
- return self.expand_prog_name(self.version)
|
|
- else:
|
|
- return ""
|
|
-
|
|
- def print_version(self, file=None):
|
|
- """print_version(file : file = stdout)
|
|
-
|
|
- Print the version message for this program (self.version) to
|
|
- 'file' (default stdout). As with print_usage(), any occurence
|
|
- of "%prog" in self.version is replaced by the current program's
|
|
- name. Does nothing if self.version is empty or undefined.
|
|
- """
|
|
- if self.version:
|
|
- print >>file, self.get_version()
|
|
-
|
|
- def format_option_help(self, formatter=None):
|
|
- if formatter is None:
|
|
- formatter = self.formatter
|
|
- formatter.store_option_strings(self)
|
|
- result = []
|
|
- result.append(formatter.format_heading(_("Options")))
|
|
- formatter.indent()
|
|
- if self.option_list:
|
|
- result.append(OptionContainer.format_option_help(self, formatter))
|
|
- result.append("\n")
|
|
- for group in self.option_groups:
|
|
- result.append(group.format_help(formatter))
|
|
- result.append("\n")
|
|
- formatter.dedent()
|
|
- # Drop the last "\n", or the header if no options or option groups:
|
|
- return "".join(result[:-1])
|
|
-
|
|
- def format_epilog(self, formatter):
|
|
- return formatter.format_epilog(self.epilog)
|
|
-
|
|
- def format_help(self, formatter=None):
|
|
- if formatter is None:
|
|
- formatter = self.formatter
|
|
- result = []
|
|
- if self.usage:
|
|
- result.append(self.get_usage() + "\n")
|
|
- if self.description:
|
|
- result.append(self.format_description(formatter) + "\n")
|
|
- result.append(self.format_option_help(formatter))
|
|
- result.append(self.format_epilog(formatter))
|
|
- return "".join(result)
|
|
-
|
|
- # used by test suite
|
|
- def _get_encoding(self, file):
|
|
- encoding = getattr(file, "encoding", None)
|
|
- if not encoding:
|
|
- encoding = sys.getdefaultencoding()
|
|
- return encoding
|
|
-
|
|
- def print_help(self, file=None):
|
|
- """print_help(file : file = stdout)
|
|
-
|
|
- Print an extended help message, listing all options and any
|
|
- help text provided with them, to 'file' (default stdout).
|
|
- """
|
|
- if file is None:
|
|
- file = sys.stdout
|
|
- encoding = self._get_encoding(file)
|
|
- file.write(self.format_help().encode(encoding, "replace"))
|
|
-
|
|
-# class OptionParser
|
|
-
|
|
-
|
|
-def _match_abbrev(s, wordmap):
|
|
- """_match_abbrev(s : string, wordmap : {string : Option}) -> string
|
|
-
|
|
- Return the string key in 'wordmap' for which 's' is an unambiguous
|
|
- abbreviation. If 's' is found to be ambiguous or doesn't match any of
|
|
- 'words', raise BadOptionError.
|
|
- """
|
|
- # Is there an exact match?
|
|
- if wordmap.has_key(s):
|
|
- return s
|
|
- else:
|
|
- # Isolate all words with s as a prefix.
|
|
- possibilities = [word for word in wordmap.keys()
|
|
- if word.startswith(s)]
|
|
- # No exact match, so there had better be just one possibility.
|
|
- if len(possibilities) == 1:
|
|
- return possibilities[0]
|
|
- elif not possibilities:
|
|
- raise BadOptionError(s)
|
|
- else:
|
|
- # More than one possible completion: ambiguous prefix.
|
|
- possibilities.sort()
|
|
- raise AmbiguousOptionError(s, possibilities)
|
|
-
|
|
-
|
|
-# Some day, there might be many Option classes. As of Optik 1.3, the
|
|
-# preferred way to instantiate Options is indirectly, via make_option(),
|
|
-# which will become a factory function when there are many Option
|
|
-# classes.
|
|
-make_option = Option
|
|
diff --git a/koan/register.py b/koan/register.py
|
|
index 9924d85..871b8ac 100755
|
|
--- a/koan/register.py
|
|
+++ b/koan/register.py
|
|
@@ -20,26 +20,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
02110-1301 USA
|
|
"""
|
|
|
|
-import random
|
|
import os
|
|
import traceback
|
|
-try:
|
|
- from optparse import OptionParser
|
|
-except:
|
|
- from opt_parse import OptionParser # importing this for backwards compat with 2.2
|
|
-try:
|
|
- import subprocess as sub_process
|
|
-except:
|
|
- import sub_process
|
|
+from optparse import OptionParser
|
|
import time
|
|
-import errno
|
|
import sys
|
|
import xmlrpclib
|
|
-import glob
|
|
import socket
|
|
import utils
|
|
import string
|
|
-import pprint
|
|
|
|
# usage: cobbler-register [--server=server] [--hostname=hostname] --profile=foo
|
|
|
|
diff --git a/koan/sub_process.py b/koan/sub_process.py
|
|
deleted file mode 100644
|
|
index bbd26c7..0000000
|
|
--- a/koan/sub_process.py
|
|
+++ /dev/null
|
|
@@ -1,1149 +0,0 @@
|
|
-# subprocess - Subprocesses with accessible I/O streams
|
|
-#
|
|
-# For more information about this module, see PEP 324.
|
|
-#
|
|
-# This module should remain compatible with Python 2.2, see PEP 291.
|
|
-#
|
|
-# Copyright (c) 2003-2005 by Peter Astrand <astrand@lysator.liu.se>
|
|
-#
|
|
-# Licensed to PSF under a Contributor Agreement.
|
|
-# See http://www.python.org/2.4/license for licensing details.
|
|
-
|
|
-r"""subprocess - Subprocesses with accessible I/O streams
|
|
-
|
|
-This module allows you to spawn processes, connect to their
|
|
-input/output/error pipes, and obtain their return codes. This module
|
|
-intends to replace several other, older modules and functions, like:
|
|
-
|
|
-os.system
|
|
-os.spawn*
|
|
-os.popen*
|
|
-popen2.*
|
|
-commands.*
|
|
-
|
|
-Information about how the subprocess module can be used to replace these
|
|
-modules and functions can be found below.
|
|
-
|
|
-
|
|
-
|
|
-Using the subprocess module
|
|
-===========================
|
|
-This module defines one class called Popen:
|
|
-
|
|
-class Popen(args, bufsize=0, executable=None,
|
|
- stdin=None, stdout=None, stderr=None,
|
|
- preexec_fn=None, close_fds=False, shell=False,
|
|
- cwd=None, env=None, universal_newlines=False,
|
|
- startupinfo=None, creationflags=0):
|
|
-
|
|
-
|
|
-Arguments are:
|
|
-
|
|
-args should be a string, or a sequence of program arguments. The
|
|
-program to execute is normally the first item in the args sequence or
|
|
-string, but can be explicitly set by using the executable argument.
|
|
-
|
|
-On UNIX, with shell=False (default): In this case, the Popen class
|
|
-uses os.execvp() to execute the child program. args should normally
|
|
-be a sequence. A string will be treated as a sequence with the string
|
|
-as the only item (the program to execute).
|
|
-
|
|
-On UNIX, with shell=True: If args is a string, it specifies the
|
|
-command string to execute through the shell. If args is a sequence,
|
|
-the first item specifies the command string, and any additional items
|
|
-will be treated as additional shell arguments.
|
|
-
|
|
-On Windows: the Popen class uses CreateProcess() to execute the child
|
|
-program, which operates on strings. If args is a sequence, it will be
|
|
-converted to a string using the list2cmdline method. Please note that
|
|
-not all MS Windows applications interpret the command line the same
|
|
-way: The list2cmdline is designed for applications using the same
|
|
-rules as the MS C runtime.
|
|
-
|
|
-bufsize, if given, has the same meaning as the corresponding argument
|
|
-to the built-in open() function: 0 means unbuffered, 1 means line
|
|
-buffered, any other positive value means use a buffer of
|
|
-(approximately) that size. A negative bufsize means to use the system
|
|
-default, which usually means fully buffered. The default value for
|
|
-bufsize is 0 (unbuffered).
|
|
-
|
|
-stdin, stdout and stderr specify the executed programs' standard
|
|
-input, standard output and standard error file handles, respectively.
|
|
-Valid values are PIPE, an existing file descriptor (a positive
|
|
-integer), an existing file object, and None. PIPE indicates that a
|
|
-new pipe to the child should be created. With None, no redirection
|
|
-will occur; the child's file handles will be inherited from the
|
|
-parent. Additionally, stderr can be STDOUT, which indicates that the
|
|
-stderr data from the applications should be captured into the same
|
|
-file handle as for stdout.
|
|
-
|
|
-If preexec_fn is set to a callable object, this object will be called
|
|
-in the child process just before the child is executed.
|
|
-
|
|
-If close_fds is true, all file descriptors except 0, 1 and 2 will be
|
|
-closed before the child process is executed.
|
|
-
|
|
-if shell is true, the specified command will be executed through the
|
|
-shell.
|
|
-
|
|
-If cwd is not None, the current directory will be changed to cwd
|
|
-before the child is executed.
|
|
-
|
|
-If env is not None, it defines the environment variables for the new
|
|
-process.
|
|
-
|
|
-If universal_newlines is true, the file objects stdout and stderr are
|
|
-opened as a text files, but lines may be terminated by any of '\n',
|
|
-the Unix end-of-line convention, '\r', the Macintosh convention or
|
|
-'\r\n', the Windows convention. All of these external representations
|
|
-are seen as '\n' by the Python program. Note: This feature is only
|
|
-available if Python is built with universal newline support (the
|
|
-default). Also, the newlines attribute of the file objects stdout,
|
|
-stdin and stderr are not updated by the communicate() method.
|
|
-
|
|
-The startupinfo and creationflags, if given, will be passed to the
|
|
-underlying CreateProcess() function. They can specify things such as
|
|
-appearance of the main window and priority for the new process.
|
|
-(Windows only)
|
|
-
|
|
-
|
|
-This module also defines two shortcut functions:
|
|
-
|
|
-call(*args, **kwargs):
|
|
- Run command with arguments. Wait for command to complete, then
|
|
- return the returncode attribute.
|
|
-
|
|
- The arguments are the same as for the Popen constructor. Example:
|
|
-
|
|
- retcode = call(["ls", "-l"])
|
|
-
|
|
-
|
|
-Exceptions
|
|
-----------
|
|
-Exceptions raised in the child process, before the new program has
|
|
-started to execute, will be re-raised in the parent. Additionally,
|
|
-the exception object will have one extra attribute called
|
|
-'child_traceback', which is a string containing traceback information
|
|
-from the childs point of view.
|
|
-
|
|
-The most common exception raised is OSError. This occurs, for
|
|
-example, when trying to execute a non-existent file. Applications
|
|
-should prepare for OSErrors.
|
|
-
|
|
-A ValueError will be raised if Popen is called with invalid arguments.
|
|
-
|
|
-
|
|
-Security
|
|
---------
|
|
-Unlike some other popen functions, this implementation will never call
|
|
-/bin/sh implicitly. This means that all characters, including shell
|
|
-metacharacters, can safely be passed to child processes.
|
|
-
|
|
-
|
|
-Popen objects
|
|
-=============
|
|
-Instances of the Popen class have the following methods:
|
|
-
|
|
-poll()
|
|
- Check if child process has terminated. Returns returncode
|
|
- attribute.
|
|
-
|
|
-wait()
|
|
- Wait for child process to terminate. Returns returncode attribute.
|
|
-
|
|
-communicate(input=None)
|
|
- Interact with process: Send data to stdin. Read data from stdout
|
|
- and stderr, until end-of-file is reached. Wait for process to
|
|
- terminate. The optional stdin argument should be a string to be
|
|
- sent to the child process, or None, if no data should be sent to
|
|
- the child.
|
|
-
|
|
- communicate() returns a tuple (stdout, stderr).
|
|
-
|
|
- Note: The data read is buffered in memory, so do not use this
|
|
- method if the data size is large or unlimited.
|
|
-
|
|
-The following attributes are also available:
|
|
-
|
|
-stdin
|
|
- If the stdin argument is PIPE, this attribute is a file object
|
|
- that provides input to the child process. Otherwise, it is None.
|
|
-
|
|
-stdout
|
|
- If the stdout argument is PIPE, this attribute is a file object
|
|
- that provides output from the child process. Otherwise, it is
|
|
- None.
|
|
-
|
|
-stderr
|
|
- If the stderr argument is PIPE, this attribute is file object that
|
|
- provides error output from the child process. Otherwise, it is
|
|
- None.
|
|
-
|
|
-pid
|
|
- The process ID of the child process.
|
|
-
|
|
-returncode
|
|
- The child return code. A None value indicates that the process
|
|
- hasn't terminated yet. A negative value -N indicates that the
|
|
- child was terminated by signal N (UNIX only).
|
|
-
|
|
-
|
|
-Replacing older functions with the subprocess module
|
|
-====================================================
|
|
-In this section, "a ==> b" means that b can be used as a replacement
|
|
-for a.
|
|
-
|
|
-Note: All functions in this section fail (more or less) silently if
|
|
-the executed program cannot be found; this module raises an OSError
|
|
-exception.
|
|
-
|
|
-In the following examples, we assume that the subprocess module is
|
|
-imported with "from subprocess import *".
|
|
-
|
|
-
|
|
-Replacing /bin/sh shell backquote
|
|
----------------------------------
|
|
-output=`mycmd myarg`
|
|
-==>
|
|
-output = Popen(["mycmd", "myarg"], stdout=PIPE).communicate()[0]
|
|
-
|
|
-
|
|
-Replacing shell pipe line
|
|
--------------------------
|
|
-output=`dmesg | grep hda`
|
|
-==>
|
|
-p1 = Popen(["dmesg"], stdout=PIPE)
|
|
-p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
|
|
-output = p2.communicate()[0]
|
|
-
|
|
-
|
|
-Replacing os.system()
|
|
----------------------
|
|
-sts = os.system("mycmd" + " myarg")
|
|
-==>
|
|
-p = Popen("mycmd" + " myarg", shell=True)
|
|
-sts = os.waitpid(p.pid, 0)
|
|
-
|
|
-Note:
|
|
-
|
|
-* Calling the program through the shell is usually not required.
|
|
-
|
|
-* It's easier to look at the returncode attribute than the
|
|
- exitstatus.
|
|
-
|
|
-A more real-world example would look like this:
|
|
-
|
|
-try:
|
|
- retcode = call("mycmd" + " myarg", shell=True)
|
|
- if retcode < 0:
|
|
- print >>sys.stderr, "Child was terminated by signal", -retcode
|
|
- else:
|
|
- print >>sys.stderr, "Child returned", retcode
|
|
-except OSError, e:
|
|
- print >>sys.stderr, "Execution failed:", e
|
|
-
|
|
-
|
|
-Replacing os.spawn*
|
|
--------------------
|
|
-P_NOWAIT example:
|
|
-
|
|
-pid = os.spawnlp(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg")
|
|
-==>
|
|
-pid = Popen(["/bin/mycmd", "myarg"]).pid
|
|
-
|
|
-
|
|
-P_WAIT example:
|
|
-
|
|
-retcode = os.spawnlp(os.P_WAIT, "/bin/mycmd", "mycmd", "myarg")
|
|
-==>
|
|
-retcode = call(["/bin/mycmd", "myarg"])
|
|
-
|
|
-
|
|
-Vector example:
|
|
-
|
|
-os.spawnvp(os.P_NOWAIT, path, args)
|
|
-==>
|
|
-Popen([path] + args[1:])
|
|
-
|
|
-
|
|
-Environment example:
|
|
-
|
|
-os.spawnlpe(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg", env)
|
|
-==>
|
|
-Popen(["/bin/mycmd", "myarg"], env={"PATH": "/usr/bin"})
|
|
-
|
|
-
|
|
-Replacing os.popen*
|
|
--------------------
|
|
-pipe = os.popen(cmd, mode='r', bufsize)
|
|
-==>
|
|
-pipe = Popen(cmd, shell=True, bufsize=bufsize, stdout=PIPE).stdout
|
|
-
|
|
-pipe = os.popen(cmd, mode='w', bufsize)
|
|
-==>
|
|
-pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE).stdin
|
|
-
|
|
-
|
|
-(child_stdin, child_stdout) = os.popen2(cmd, mode, bufsize)
|
|
-==>
|
|
-p = Popen(cmd, shell=True, bufsize=bufsize,
|
|
- stdin=PIPE, stdout=PIPE, close_fds=True)
|
|
-(child_stdin, child_stdout) = (p.stdin, p.stdout)
|
|
-
|
|
-
|
|
-(child_stdin,
|
|
- child_stdout,
|
|
- child_stderr) = os.popen3(cmd, mode, bufsize)
|
|
-==>
|
|
-p = Popen(cmd, shell=True, bufsize=bufsize,
|
|
- stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
|
|
-(child_stdin,
|
|
- child_stdout,
|
|
- child_stderr) = (p.stdin, p.stdout, p.stderr)
|
|
-
|
|
-
|
|
-(child_stdin, child_stdout_and_stderr) = os.popen4(cmd, mode, bufsize)
|
|
-==>
|
|
-p = Popen(cmd, shell=True, bufsize=bufsize,
|
|
- stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
|
|
-(child_stdin, child_stdout_and_stderr) = (p.stdin, p.stdout)
|
|
-
|
|
-
|
|
-Replacing popen2.*
|
|
-------------------
|
|
-Note: If the cmd argument to popen2 functions is a string, the command
|
|
-is executed through /bin/sh. If it is a list, the command is directly
|
|
-executed.
|
|
-
|
|
-(child_stdout, child_stdin) = popen2.popen2("somestring", bufsize, mode)
|
|
-==>
|
|
-p = Popen(["somestring"], shell=True, bufsize=bufsize
|
|
- stdin=PIPE, stdout=PIPE, close_fds=True)
|
|
-(child_stdout, child_stdin) = (p.stdout, p.stdin)
|
|
-
|
|
-
|
|
-(child_stdout, child_stdin) = popen2.popen2(["mycmd", "myarg"], bufsize, mode)
|
|
-==>
|
|
-p = Popen(["mycmd", "myarg"], bufsize=bufsize,
|
|
- stdin=PIPE, stdout=PIPE, close_fds=True)
|
|
-(child_stdout, child_stdin) = (p.stdout, p.stdin)
|
|
-
|
|
-The popen2.Popen3 and popen3.Popen4 basically works as subprocess.Popen,
|
|
-except that:
|
|
-
|
|
-* subprocess.Popen raises an exception if the execution fails
|
|
-* the capturestderr argument is replaced with the stderr argument.
|
|
-* stdin=PIPE and stdout=PIPE must be specified.
|
|
-* popen2 closes all filedescriptors by default, but you have to specify
|
|
- close_fds=True with subprocess.Popen.
|
|
-
|
|
-
|
|
-"""
|
|
-
|
|
-import sys
|
|
-mswindows = (sys.platform == "win32")
|
|
-
|
|
-import os
|
|
-import types
|
|
-import traceback
|
|
-
|
|
-if mswindows:
|
|
- import threading
|
|
- import msvcrt
|
|
- if 0: # <-- change this to use pywin32 instead of the _subprocess driver
|
|
- import pywintypes
|
|
- from win32api import GetStdHandle, STD_INPUT_HANDLE, \
|
|
- STD_OUTPUT_HANDLE, STD_ERROR_HANDLE
|
|
- from win32api import GetCurrentProcess, DuplicateHandle, \
|
|
- GetModuleFileName, GetVersion
|
|
- from win32con import DUPLICATE_SAME_ACCESS, SW_HIDE
|
|
- from win32pipe import CreatePipe
|
|
- from win32process import CreateProcess, STARTUPINFO, \
|
|
- GetExitCodeProcess, STARTF_USESTDHANDLES, \
|
|
- STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE
|
|
- from win32event import WaitForSingleObject, INFINITE, WAIT_OBJECT_0
|
|
- else:
|
|
- from _subprocess import *
|
|
- class STARTUPINFO:
|
|
- dwFlags = 0
|
|
- hStdInput = None
|
|
- hStdOutput = None
|
|
- hStdError = None
|
|
- class pywintypes:
|
|
- error = IOError
|
|
-else:
|
|
- import select
|
|
- import errno
|
|
- import fcntl
|
|
- import pickle
|
|
-
|
|
-__all__ = ["Popen", "PIPE", "STDOUT", "call"]
|
|
-
|
|
-try:
|
|
- MAXFD = os.sysconf("SC_OPEN_MAX")
|
|
-except:
|
|
- MAXFD = 256
|
|
-
|
|
-# True/False does not exist on 2.2.0
|
|
-try:
|
|
- False
|
|
-except NameError:
|
|
- False = 0
|
|
- True = 1
|
|
-
|
|
-_active = []
|
|
-
|
|
-def _cleanup():
|
|
- for inst in _active[:]:
|
|
- inst.poll()
|
|
-
|
|
-PIPE = -1
|
|
-STDOUT = -2
|
|
-
|
|
-
|
|
-def call(*args, **kwargs):
|
|
- """Run command with arguments. Wait for command to complete, then
|
|
- return the returncode attribute.
|
|
-
|
|
- The arguments are the same as for the Popen constructor. Example:
|
|
-
|
|
- retcode = call(["ls", "-l"])
|
|
- """
|
|
- return Popen(*args, **kwargs).wait()
|
|
-
|
|
-
|
|
-def list2cmdline(seq):
|
|
- """
|
|
- Translate a sequence of arguments into a command line
|
|
- string, using the same rules as the MS C runtime:
|
|
-
|
|
- 1) Arguments are delimited by white space, which is either a
|
|
- space or a tab.
|
|
-
|
|
- 2) A string surrounded by double quotation marks is
|
|
- interpreted as a single argument, regardless of white space
|
|
- contained within. A quoted string can be embedded in an
|
|
- argument.
|
|
-
|
|
- 3) A double quotation mark preceded by a backslash is
|
|
- interpreted as a literal double quotation mark.
|
|
-
|
|
- 4) Backslashes are interpreted literally, unless they
|
|
- immediately precede a double quotation mark.
|
|
-
|
|
- 5) If backslashes immediately precede a double quotation mark,
|
|
- every pair of backslashes is interpreted as a literal
|
|
- backslash. If the number of backslashes is odd, the last
|
|
- backslash escapes the next double quotation mark as
|
|
- described in rule 3.
|
|
- """
|
|
-
|
|
- # See
|
|
- # http://msdn.microsoft.com/library/en-us/vccelng/htm/progs_12.asp
|
|
- result = []
|
|
- needquote = False
|
|
- for arg in seq:
|
|
- bs_buf = []
|
|
-
|
|
- # Add a space to separate this argument from the others
|
|
- if result:
|
|
- result.append(' ')
|
|
-
|
|
- needquote = (" " in arg) or ("\t" in arg)
|
|
- if needquote:
|
|
- result.append('"')
|
|
-
|
|
- for c in arg:
|
|
- if c == '\\':
|
|
- # Don't know if we need to double yet.
|
|
- bs_buf.append(c)
|
|
- elif c == '"':
|
|
- # Double backspaces.
|
|
- result.append('\\' * len(bs_buf)*2)
|
|
- bs_buf = []
|
|
- result.append('\\"')
|
|
- else:
|
|
- # Normal char
|
|
- if bs_buf:
|
|
- result.extend(bs_buf)
|
|
- bs_buf = []
|
|
- result.append(c)
|
|
-
|
|
- # Add remaining backspaces, if any.
|
|
- if bs_buf:
|
|
- result.extend(bs_buf)
|
|
-
|
|
- if needquote:
|
|
- result.extend(bs_buf)
|
|
- result.append('"')
|
|
-
|
|
- return ''.join(result)
|
|
-
|
|
-
|
|
-class Popen(object):
|
|
- def __init__(self, args, bufsize=0, executable=None,
|
|
- stdin=None, stdout=None, stderr=None,
|
|
- preexec_fn=None, close_fds=False, shell=False,
|
|
- cwd=None, env=None, universal_newlines=False,
|
|
- startupinfo=None, creationflags=0):
|
|
- """Create new Popen instance."""
|
|
- _cleanup()
|
|
-
|
|
- if not isinstance(bufsize, (int, long)):
|
|
- raise TypeError("bufsize must be an integer")
|
|
-
|
|
- if mswindows:
|
|
- if preexec_fn is not None:
|
|
- raise ValueError("preexec_fn is not supported on Windows "
|
|
- "platforms")
|
|
- if close_fds:
|
|
- raise ValueError("close_fds is not supported on Windows "
|
|
- "platforms")
|
|
- else:
|
|
- # POSIX
|
|
- if startupinfo is not None:
|
|
- raise ValueError("startupinfo is only supported on Windows "
|
|
- "platforms")
|
|
- if creationflags != 0:
|
|
- raise ValueError("creationflags is only supported on Windows "
|
|
- "platforms")
|
|
-
|
|
- self.stdin = None
|
|
- self.stdout = None
|
|
- self.stderr = None
|
|
- self.pid = None
|
|
- self.returncode = None
|
|
- self.universal_newlines = universal_newlines
|
|
-
|
|
- # Input and output objects. The general principle is like
|
|
- # this:
|
|
- #
|
|
- # Parent Child
|
|
- # ------ -----
|
|
- # p2cwrite ---stdin---> p2cread
|
|
- # c2pread <--stdout--- c2pwrite
|
|
- # errread <--stderr--- errwrite
|
|
- #
|
|
- # On POSIX, the child objects are file descriptors. On
|
|
- # Windows, these are Windows file handles. The parent objects
|
|
- # are file descriptors on both platforms. The parent objects
|
|
- # are None when not using PIPEs. The child objects are None
|
|
- # when not redirecting.
|
|
-
|
|
- (p2cread, p2cwrite,
|
|
- c2pread, c2pwrite,
|
|
- errread, errwrite) = self._get_handles(stdin, stdout, stderr)
|
|
-
|
|
- self._execute_child(args, executable, preexec_fn, close_fds,
|
|
- cwd, env, universal_newlines,
|
|
- startupinfo, creationflags, shell,
|
|
- p2cread, p2cwrite,
|
|
- c2pread, c2pwrite,
|
|
- errread, errwrite)
|
|
-
|
|
- if p2cwrite:
|
|
- self.stdin = os.fdopen(p2cwrite, 'wb', bufsize)
|
|
- if c2pread:
|
|
- if universal_newlines:
|
|
- self.stdout = os.fdopen(c2pread, 'rU', bufsize)
|
|
- else:
|
|
- self.stdout = os.fdopen(c2pread, 'rb', bufsize)
|
|
- if errread:
|
|
- if universal_newlines:
|
|
- self.stderr = os.fdopen(errread, 'rU', bufsize)
|
|
- else:
|
|
- self.stderr = os.fdopen(errread, 'rb', bufsize)
|
|
-
|
|
- _active.append(self)
|
|
-
|
|
-
|
|
- def _translate_newlines(self, data):
|
|
- data = data.replace("\r\n", "\n")
|
|
- data = data.replace("\r", "\n")
|
|
- return data
|
|
-
|
|
-
|
|
- if mswindows:
|
|
- #
|
|
- # Windows methods
|
|
- #
|
|
- def _get_handles(self, stdin, stdout, stderr):
|
|
- """Construct and return tupel with IO objects:
|
|
- p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite
|
|
- """
|
|
- if stdin == None and stdout == None and stderr == None:
|
|
- return (None, None, None, None, None, None)
|
|
-
|
|
- p2cread, p2cwrite = None, None
|
|
- c2pread, c2pwrite = None, None
|
|
- errread, errwrite = None, None
|
|
-
|
|
- if stdin == None:
|
|
- p2cread = GetStdHandle(STD_INPUT_HANDLE)
|
|
- elif stdin == PIPE:
|
|
- p2cread, p2cwrite = CreatePipe(None, 0)
|
|
- # Detach and turn into fd
|
|
- p2cwrite = p2cwrite.Detach()
|
|
- p2cwrite = msvcrt.open_osfhandle(p2cwrite, 0)
|
|
- elif type(stdin) == types.IntType:
|
|
- p2cread = msvcrt.get_osfhandle(stdin)
|
|
- else:
|
|
- # Assuming file-like object
|
|
- p2cread = msvcrt.get_osfhandle(stdin.fileno())
|
|
- p2cread = self._make_inheritable(p2cread)
|
|
-
|
|
- if stdout == None:
|
|
- c2pwrite = GetStdHandle(STD_OUTPUT_HANDLE)
|
|
- elif stdout == PIPE:
|
|
- c2pread, c2pwrite = CreatePipe(None, 0)
|
|
- # Detach and turn into fd
|
|
- c2pread = c2pread.Detach()
|
|
- c2pread = msvcrt.open_osfhandle(c2pread, 0)
|
|
- elif type(stdout) == types.IntType:
|
|
- c2pwrite = msvcrt.get_osfhandle(stdout)
|
|
- else:
|
|
- # Assuming file-like object
|
|
- c2pwrite = msvcrt.get_osfhandle(stdout.fileno())
|
|
- c2pwrite = self._make_inheritable(c2pwrite)
|
|
-
|
|
- if stderr == None:
|
|
- errwrite = GetStdHandle(STD_ERROR_HANDLE)
|
|
- elif stderr == PIPE:
|
|
- errread, errwrite = CreatePipe(None, 0)
|
|
- # Detach and turn into fd
|
|
- errread = errread.Detach()
|
|
- errread = msvcrt.open_osfhandle(errread, 0)
|
|
- elif stderr == STDOUT:
|
|
- errwrite = c2pwrite
|
|
- elif type(stderr) == types.IntType:
|
|
- errwrite = msvcrt.get_osfhandle(stderr)
|
|
- else:
|
|
- # Assuming file-like object
|
|
- errwrite = msvcrt.get_osfhandle(stderr.fileno())
|
|
- errwrite = self._make_inheritable(errwrite)
|
|
-
|
|
- return (p2cread, p2cwrite,
|
|
- c2pread, c2pwrite,
|
|
- errread, errwrite)
|
|
-
|
|
-
|
|
- def _make_inheritable(self, handle):
|
|
- """Return a duplicate of handle, which is inheritable"""
|
|
- return DuplicateHandle(GetCurrentProcess(), handle,
|
|
- GetCurrentProcess(), 0, 1,
|
|
- DUPLICATE_SAME_ACCESS)
|
|
-
|
|
-
|
|
- def _find_w9xpopen(self):
|
|
- """Find and return absolut path to w9xpopen.exe"""
|
|
- w9xpopen = os.path.join(os.path.dirname(GetModuleFileName(0)),
|
|
- "w9xpopen.exe")
|
|
- if not os.path.exists(w9xpopen):
|
|
- # Eeek - file-not-found - possibly an embedding
|
|
- # situation - see if we can locate it in sys.exec_prefix
|
|
- w9xpopen = os.path.join(os.path.dirname(sys.exec_prefix),
|
|
- "w9xpopen.exe")
|
|
- if not os.path.exists(w9xpopen):
|
|
- raise RuntimeError("Cannot locate w9xpopen.exe, which is "
|
|
- "needed for Popen to work with your "
|
|
- "shell or platform.")
|
|
- return w9xpopen
|
|
-
|
|
-
|
|
- def _execute_child(self, args, executable, preexec_fn, close_fds,
|
|
- cwd, env, universal_newlines,
|
|
- startupinfo, creationflags, shell,
|
|
- p2cread, p2cwrite,
|
|
- c2pread, c2pwrite,
|
|
- errread, errwrite):
|
|
- """Execute program (MS Windows version)"""
|
|
-
|
|
- if not isinstance(args, types.StringTypes):
|
|
- args = list2cmdline(args)
|
|
-
|
|
- # Process startup details
|
|
- default_startupinfo = STARTUPINFO()
|
|
- if startupinfo == None:
|
|
- startupinfo = default_startupinfo
|
|
- if not None in (p2cread, c2pwrite, errwrite):
|
|
- startupinfo.dwFlags |= STARTF_USESTDHANDLES
|
|
- startupinfo.hStdInput = p2cread
|
|
- startupinfo.hStdOutput = c2pwrite
|
|
- startupinfo.hStdError = errwrite
|
|
-
|
|
- if shell:
|
|
- default_startupinfo.dwFlags |= STARTF_USESHOWWINDOW
|
|
- default_startupinfo.wShowWindow = SW_HIDE
|
|
- comspec = os.environ.get("COMSPEC", "cmd.exe")
|
|
- args = comspec + " /c " + args
|
|
- if (GetVersion() >= 0x80000000L or
|
|
- os.path.basename(comspec).lower() == "command.com"):
|
|
- # Win9x, or using command.com on NT. We need to
|
|
- # use the w9xpopen intermediate program. For more
|
|
- # information, see KB Q150956
|
|
- # (http://web.archive.org/web/20011105084002/http://support.microsoft.com/support/kb/articles/Q150/9/56.asp)
|
|
- w9xpopen = self._find_w9xpopen()
|
|
- args = '"%s" %s' % (w9xpopen, args)
|
|
- # Not passing CREATE_NEW_CONSOLE has been known to
|
|
- # cause random failures on win9x. Specifically a
|
|
- # dialog: "Your program accessed mem currently in
|
|
- # use at xxx" and a hopeful warning about the
|
|
- # stability of your system. Cost is Ctrl+C wont
|
|
- # kill children.
|
|
- creationflags |= CREATE_NEW_CONSOLE
|
|
-
|
|
- # Start the process
|
|
- try:
|
|
- hp, ht, pid, tid = CreateProcess(executable, args,
|
|
- # no special security
|
|
- None, None,
|
|
- # must inherit handles to pass std
|
|
- # handles
|
|
- 1,
|
|
- creationflags,
|
|
- env,
|
|
- cwd,
|
|
- startupinfo)
|
|
- except pywintypes.error, e:
|
|
- # Translate pywintypes.error to WindowsError, which is
|
|
- # a subclass of OSError. FIXME: We should really
|
|
- # translate errno using _sys_errlist (or simliar), but
|
|
- # how can this be done from Python?
|
|
- raise WindowsError(*e.args)
|
|
-
|
|
- # Retain the process handle, but close the thread handle
|
|
- self._handle = hp
|
|
- self.pid = pid
|
|
- ht.Close()
|
|
-
|
|
- # Child is launched. Close the parent's copy of those pipe
|
|
- # handles that only the child should have open. You need
|
|
- # to make sure that no handles to the write end of the
|
|
- # output pipe are maintained in this process or else the
|
|
- # pipe will not close when the child process exits and the
|
|
- # ReadFile will hang.
|
|
- if p2cread != None:
|
|
- p2cread.Close()
|
|
- if c2pwrite != None:
|
|
- c2pwrite.Close()
|
|
- if errwrite != None:
|
|
- errwrite.Close()
|
|
-
|
|
-
|
|
- def poll(self):
|
|
- """Check if child process has terminated. Returns returncode
|
|
- attribute."""
|
|
- if self.returncode == None:
|
|
- if WaitForSingleObject(self._handle, 0) == WAIT_OBJECT_0:
|
|
- self.returncode = GetExitCodeProcess(self._handle)
|
|
- _active.remove(self)
|
|
- return self.returncode
|
|
-
|
|
-
|
|
- def wait(self):
|
|
- """Wait for child process to terminate. Returns returncode
|
|
- attribute."""
|
|
- if self.returncode == None:
|
|
- obj = WaitForSingleObject(self._handle, INFINITE)
|
|
- self.returncode = GetExitCodeProcess(self._handle)
|
|
- _active.remove(self)
|
|
- return self.returncode
|
|
-
|
|
-
|
|
- def _readerthread(self, fh, buffer):
|
|
- buffer.append(fh.read())
|
|
-
|
|
-
|
|
- def communicate(self, input=None):
|
|
- """Interact with process: Send data to stdin. Read data from
|
|
- stdout and stderr, until end-of-file is reached. Wait for
|
|
- process to terminate. The optional input argument should be a
|
|
- string to be sent to the child process, or None, if no data
|
|
- should be sent to the child.
|
|
-
|
|
- communicate() returns a tuple (stdout, stderr)."""
|
|
- stdout = None # Return
|
|
- stderr = None # Return
|
|
-
|
|
- if self.stdout:
|
|
- stdout = []
|
|
- stdout_thread = threading.Thread(target=self._readerthread,
|
|
- args=(self.stdout, stdout))
|
|
- stdout_thread.setDaemon(True)
|
|
- stdout_thread.start()
|
|
- if self.stderr:
|
|
- stderr = []
|
|
- stderr_thread = threading.Thread(target=self._readerthread,
|
|
- args=(self.stderr, stderr))
|
|
- stderr_thread.setDaemon(True)
|
|
- stderr_thread.start()
|
|
-
|
|
- if self.stdin:
|
|
- if input != None:
|
|
- self.stdin.write(input)
|
|
- self.stdin.close()
|
|
-
|
|
- if self.stdout:
|
|
- stdout_thread.join()
|
|
- if self.stderr:
|
|
- stderr_thread.join()
|
|
-
|
|
- # All data exchanged. Translate lists into strings.
|
|
- if stdout != None:
|
|
- stdout = stdout[0]
|
|
- if stderr != None:
|
|
- stderr = stderr[0]
|
|
-
|
|
- # Translate newlines, if requested. We cannot let the file
|
|
- # object do the translation: It is based on stdio, which is
|
|
- # impossible to combine with select (unless forcing no
|
|
- # buffering).
|
|
- if self.universal_newlines and hasattr(open, 'newlines'):
|
|
- if stdout:
|
|
- stdout = self._translate_newlines(stdout)
|
|
- if stderr:
|
|
- stderr = self._translate_newlines(stderr)
|
|
-
|
|
- self.wait()
|
|
- return (stdout, stderr)
|
|
-
|
|
- else:
|
|
- #
|
|
- # POSIX methods
|
|
- #
|
|
- def _get_handles(self, stdin, stdout, stderr):
|
|
- """Construct and return tupel with IO objects:
|
|
- p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite
|
|
- """
|
|
- p2cread, p2cwrite = None, None
|
|
- c2pread, c2pwrite = None, None
|
|
- errread, errwrite = None, None
|
|
-
|
|
- if stdin == None:
|
|
- pass
|
|
- elif stdin == PIPE:
|
|
- p2cread, p2cwrite = os.pipe()
|
|
- elif type(stdin) == types.IntType:
|
|
- p2cread = stdin
|
|
- else:
|
|
- # Assuming file-like object
|
|
- p2cread = stdin.fileno()
|
|
-
|
|
- if stdout == None:
|
|
- pass
|
|
- elif stdout == PIPE:
|
|
- c2pread, c2pwrite = os.pipe()
|
|
- elif type(stdout) == types.IntType:
|
|
- c2pwrite = stdout
|
|
- else:
|
|
- # Assuming file-like object
|
|
- c2pwrite = stdout.fileno()
|
|
-
|
|
- if stderr == None:
|
|
- pass
|
|
- elif stderr == PIPE:
|
|
- errread, errwrite = os.pipe()
|
|
- elif stderr == STDOUT:
|
|
- errwrite = c2pwrite
|
|
- elif type(stderr) == types.IntType:
|
|
- errwrite = stderr
|
|
- else:
|
|
- # Assuming file-like object
|
|
- errwrite = stderr.fileno()
|
|
-
|
|
- return (p2cread, p2cwrite,
|
|
- c2pread, c2pwrite,
|
|
- errread, errwrite)
|
|
-
|
|
-
|
|
- def _set_cloexec_flag(self, fd):
|
|
- try:
|
|
- cloexec_flag = fcntl.FD_CLOEXEC
|
|
- except AttributeError:
|
|
- cloexec_flag = 1
|
|
-
|
|
- old = fcntl.fcntl(fd, fcntl.F_GETFD)
|
|
- fcntl.fcntl(fd, fcntl.F_SETFD, old | cloexec_flag)
|
|
-
|
|
-
|
|
- def _close_fds(self, but):
|
|
- for i in range(3, MAXFD):
|
|
- if i == but:
|
|
- continue
|
|
- try:
|
|
- os.close(i)
|
|
- except:
|
|
- pass
|
|
-
|
|
-
|
|
- def _execute_child(self, args, executable, preexec_fn, close_fds,
|
|
- cwd, env, universal_newlines,
|
|
- startupinfo, creationflags, shell,
|
|
- p2cread, p2cwrite,
|
|
- c2pread, c2pwrite,
|
|
- errread, errwrite):
|
|
- """Execute program (POSIX version)"""
|
|
-
|
|
- if isinstance(args, types.StringTypes):
|
|
- args = [args]
|
|
-
|
|
- if shell:
|
|
- args = ["/bin/sh", "-c"] + args
|
|
-
|
|
- if executable == None:
|
|
- executable = args[0]
|
|
-
|
|
- # For transferring possible exec failure from child to parent
|
|
- # The first char specifies the exception type: 0 means
|
|
- # OSError, 1 means some other error.
|
|
- errpipe_read, errpipe_write = os.pipe()
|
|
- self._set_cloexec_flag(errpipe_write)
|
|
-
|
|
- self.pid = os.fork()
|
|
- if self.pid == 0:
|
|
- # Child
|
|
- try:
|
|
- # Close parent's pipe ends
|
|
- if p2cwrite:
|
|
- os.close(p2cwrite)
|
|
- if c2pread:
|
|
- os.close(c2pread)
|
|
- if errread:
|
|
- os.close(errread)
|
|
- os.close(errpipe_read)
|
|
-
|
|
- # Dup fds for child
|
|
- if p2cread:
|
|
- os.dup2(p2cread, 0)
|
|
- if c2pwrite:
|
|
- os.dup2(c2pwrite, 1)
|
|
- if errwrite:
|
|
- os.dup2(errwrite, 2)
|
|
-
|
|
- # Close pipe fds. Make sure we doesn't close the same
|
|
- # fd more than once.
|
|
- if p2cread:
|
|
- os.close(p2cread)
|
|
- if c2pwrite and c2pwrite not in (p2cread,):
|
|
- os.close(c2pwrite)
|
|
- if errwrite and errwrite not in (p2cread, c2pwrite):
|
|
- os.close(errwrite)
|
|
-
|
|
- # Close all other fds, if asked for
|
|
- if close_fds:
|
|
- self._close_fds(but=errpipe_write)
|
|
-
|
|
- if cwd != None:
|
|
- os.chdir(cwd)
|
|
-
|
|
- if preexec_fn:
|
|
- apply(preexec_fn)
|
|
-
|
|
- if env == None:
|
|
- os.execvp(executable, args)
|
|
- else:
|
|
- os.execvpe(executable, args, env)
|
|
-
|
|
- except:
|
|
- exc_type, exc_value, tb = sys.exc_info()
|
|
- # Save the traceback and attach it to the exception object
|
|
- exc_lines = traceback.format_exception(exc_type,
|
|
- exc_value,
|
|
- tb)
|
|
- exc_value.child_traceback = ''.join(exc_lines)
|
|
- os.write(errpipe_write, pickle.dumps(exc_value))
|
|
-
|
|
- # This exitcode won't be reported to applications, so it
|
|
- # really doesn't matter what we return.
|
|
- os._exit(255)
|
|
-
|
|
- # Parent
|
|
- os.close(errpipe_write)
|
|
- if p2cread and p2cwrite:
|
|
- os.close(p2cread)
|
|
- if c2pwrite and c2pread:
|
|
- os.close(c2pwrite)
|
|
- if errwrite and errread:
|
|
- os.close(errwrite)
|
|
-
|
|
- # Wait for exec to fail or succeed; possibly raising exception
|
|
- data = os.read(errpipe_read, 1048576) # Exceptions limited to 1 MB
|
|
- os.close(errpipe_read)
|
|
- if data != "":
|
|
- os.waitpid(self.pid, 0)
|
|
- child_exception = pickle.loads(data)
|
|
- raise child_exception
|
|
-
|
|
-
|
|
- def _handle_exitstatus(self, sts):
|
|
- if os.WIFSIGNALED(sts):
|
|
- self.returncode = -os.WTERMSIG(sts)
|
|
- elif os.WIFEXITED(sts):
|
|
- self.returncode = os.WEXITSTATUS(sts)
|
|
- else:
|
|
- # Should never happen
|
|
- raise RuntimeError("Unknown child exit status!")
|
|
-
|
|
- _active.remove(self)
|
|
-
|
|
-
|
|
- def poll(self):
|
|
- """Check if child process has terminated. Returns returncode
|
|
- attribute."""
|
|
- if self.returncode == None:
|
|
- try:
|
|
- pid, sts = os.waitpid(self.pid, os.WNOHANG)
|
|
- if pid == self.pid:
|
|
- self._handle_exitstatus(sts)
|
|
- except os.error:
|
|
- pass
|
|
- return self.returncode
|
|
-
|
|
-
|
|
- def wait(self):
|
|
- """Wait for child process to terminate. Returns returncode
|
|
- attribute."""
|
|
- if self.returncode == None:
|
|
- pid, sts = os.waitpid(self.pid, 0)
|
|
- self._handle_exitstatus(sts)
|
|
- return self.returncode
|
|
-
|
|
-
|
|
- def communicate(self, input=None):
|
|
- """Interact with process: Send data to stdin. Read data from
|
|
- stdout and stderr, until end-of-file is reached. Wait for
|
|
- process to terminate. The optional input argument should be a
|
|
- string to be sent to the child process, or None, if no data
|
|
- should be sent to the child.
|
|
-
|
|
- communicate() returns a tuple (stdout, stderr)."""
|
|
- read_set = []
|
|
- write_set = []
|
|
- stdout = None # Return
|
|
- stderr = None # Return
|
|
-
|
|
- if self.stdin:
|
|
- # Flush stdio buffer. This might block, if the user has
|
|
- # been writing to .stdin in an uncontrolled fashion.
|
|
- self.stdin.flush()
|
|
- if input:
|
|
- write_set.append(self.stdin)
|
|
- else:
|
|
- self.stdin.close()
|
|
- if self.stdout:
|
|
- read_set.append(self.stdout)
|
|
- stdout = []
|
|
- if self.stderr:
|
|
- read_set.append(self.stderr)
|
|
- stderr = []
|
|
-
|
|
- while read_set or write_set:
|
|
- rlist, wlist, xlist = select.select(read_set, write_set, [])
|
|
-
|
|
- if self.stdin in wlist:
|
|
- # When select has indicated that the file is writable,
|
|
- # we can write up to PIPE_BUF bytes without risk
|
|
- # blocking. POSIX defines PIPE_BUF >= 512
|
|
- bytes_written = os.write(self.stdin.fileno(), input[:512])
|
|
- input = input[bytes_written:]
|
|
- if not input:
|
|
- self.stdin.close()
|
|
- write_set.remove(self.stdin)
|
|
-
|
|
- if self.stdout in rlist:
|
|
- data = os.read(self.stdout.fileno(), 1024)
|
|
- if data == "":
|
|
- self.stdout.close()
|
|
- read_set.remove(self.stdout)
|
|
- stdout.append(data)
|
|
-
|
|
- if self.stderr in rlist:
|
|
- data = os.read(self.stderr.fileno(), 1024)
|
|
- if data == "":
|
|
- self.stderr.close()
|
|
- read_set.remove(self.stderr)
|
|
- stderr.append(data)
|
|
-
|
|
- # All data exchanged. Translate lists into strings.
|
|
- if stdout != None:
|
|
- stdout = ''.join(stdout)
|
|
- if stderr != None:
|
|
- stderr = ''.join(stderr)
|
|
-
|
|
- # Translate newlines, if requested. We cannot let the file
|
|
- # object do the translation: It is based on stdio, which is
|
|
- # impossible to combine with select (unless forcing no
|
|
- # buffering).
|
|
- if self.universal_newlines and hasattr(open, 'newlines'):
|
|
- if stdout:
|
|
- stdout = self._translate_newlines(stdout)
|
|
- if stderr:
|
|
- stderr = self._translate_newlines(stderr)
|
|
-
|
|
- self.wait()
|
|
- return (stdout, stderr)
|
|
-
|
|
-
|
|
-def _demo_posix():
|
|
- #
|
|
- # Example 1: Simple redirection: Get process list
|
|
- #
|
|
- plist = Popen(["ps"], stdout=PIPE).communicate()[0]
|
|
- print "Process list:"
|
|
- print plist
|
|
-
|
|
- #
|
|
- # Example 2: Change uid before executing child
|
|
- #
|
|
- if os.getuid() == 0:
|
|
- p = Popen(["id"], preexec_fn=lambda: os.setuid(100))
|
|
- p.wait()
|
|
-
|
|
- #
|
|
- # Example 3: Connecting several subprocesses
|
|
- #
|
|
- print "Looking for 'hda'..."
|
|
- p1 = Popen(["dmesg"], stdout=PIPE)
|
|
- p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
|
|
- print repr(p2.communicate()[0])
|
|
-
|
|
- #
|
|
- # Example 4: Catch execution error
|
|
- #
|
|
- print
|
|
- print "Trying a weird file..."
|
|
- try:
|
|
- print Popen(["/this/path/does/not/exist"]).communicate()
|
|
- except OSError, e:
|
|
- if e.errno == errno.ENOENT:
|
|
- print "The file didn't exist. I thought so..."
|
|
- print "Child traceback:"
|
|
- print e.child_traceback
|
|
- else:
|
|
- print "Error", e.errno
|
|
- else:
|
|
- print >>sys.stderr, "Gosh. No error."
|
|
-
|
|
-
|
|
-def _demo_windows():
|
|
- #
|
|
- # Example 1: Connecting several subprocesses
|
|
- #
|
|
- print "Looking for 'PROMPT' in set output..."
|
|
- p1 = Popen("set", stdout=PIPE, shell=True)
|
|
- p2 = Popen('find "PROMPT"', stdin=p1.stdout, stdout=PIPE)
|
|
- print repr(p2.communicate()[0])
|
|
-
|
|
- #
|
|
- # Example 2: Simple execution of program
|
|
- #
|
|
- print "Executing calc..."
|
|
- p = Popen("calc")
|
|
- p.wait()
|
|
-
|
|
-
|
|
-if __name__ == "__main__":
|
|
- if mswindows:
|
|
- _demo_windows()
|
|
- else:
|
|
- _demo_posix()
|
|
diff --git a/koan/text_wrap.py b/koan/text_wrap.py
|
|
deleted file mode 100644
|
|
index 2035463..0000000
|
|
--- a/koan/text_wrap.py
|
|
+++ /dev/null
|
|
@@ -1,354 +0,0 @@
|
|
-"""Text wrapping and filling.
|
|
-(note: repackaged in koan because it's not present in RHEL3)
|
|
-"""
|
|
-
|
|
-# Copyright (C) 1999-2001 Gregory P. Ward.
|
|
-# Copyright (C) 2002, 2003 Python Software Foundation.
|
|
-# Written by Greg Ward <gward@python.net>
|
|
-
|
|
-__revision__ = "$Id: textwrap.py,v 1.32.8.2 2004/05/13 01:48:15 gward Exp $"
|
|
-
|
|
-import string, re
|
|
-
|
|
-# Do the right thing with boolean values for all known Python versions
|
|
-# (so this module can be copied to projects that don't depend on Python
|
|
-# 2.3, e.g. Optik and Docutils).
|
|
-try:
|
|
- True, False
|
|
-except NameError:
|
|
- (True, False) = (1, 0)
|
|
-
|
|
-__all__ = ['TextWrapper', 'wrap', 'fill']
|
|
-
|
|
-# Hardcode the recognized whitespace characters to the US-ASCII
|
|
-# whitespace characters. The main reason for doing this is that in
|
|
-# ISO-8859-1, 0xa0 is non-breaking whitespace, so in certain locales
|
|
-# that character winds up in string.whitespace. Respecting
|
|
-# string.whitespace in those cases would 1) make textwrap treat 0xa0 the
|
|
-# same as any other whitespace char, which is clearly wrong (it's a
|
|
-# *non-breaking* space), 2) possibly cause problems with Unicode,
|
|
-# since 0xa0 is not in range(128).
|
|
-_whitespace = '\t\n\x0b\x0c\r '
|
|
-
|
|
-class TextWrapper:
|
|
- """
|
|
- Object for wrapping/filling text. The public interface consists of
|
|
- the wrap() and fill() methods; the other methods are just there for
|
|
- subclasses to override in order to tweak the default behaviour.
|
|
- If you want to completely replace the main wrapping algorithm,
|
|
- you'll probably have to override _wrap_chunks().
|
|
-
|
|
- Several instance attributes control various aspects of wrapping:
|
|
- width (default: 70)
|
|
- the maximum width of wrapped lines (unless break_long_words
|
|
- is false)
|
|
- initial_indent (default: "")
|
|
- string that will be prepended to the first line of wrapped
|
|
- output. Counts towards the line's width.
|
|
- subsequent_indent (default: "")
|
|
- string that will be prepended to all lines save the first
|
|
- of wrapped output; also counts towards each line's width.
|
|
- expand_tabs (default: true)
|
|
- Expand tabs in input text to spaces before further processing.
|
|
- Each tab will become 1 .. 8 spaces, depending on its position in
|
|
- its line. If false, each tab is treated as a single character.
|
|
- replace_whitespace (default: true)
|
|
- Replace all whitespace characters in the input text by spaces
|
|
- after tab expansion. Note that if expand_tabs is false and
|
|
- replace_whitespace is true, every tab will be converted to a
|
|
- single space!
|
|
- fix_sentence_endings (default: false)
|
|
- Ensure that sentence-ending punctuation is always followed
|
|
- by two spaces. Off by default because the algorithm is
|
|
- (unavoidably) imperfect.
|
|
- break_long_words (default: true)
|
|
- Break words longer than 'width'. If false, those words will not
|
|
- be broken, and some lines might be longer than 'width'.
|
|
- """
|
|
-
|
|
- whitespace_trans = string.maketrans(_whitespace, ' ' * len(_whitespace))
|
|
-
|
|
- unicode_whitespace_trans = {}
|
|
- uspace = ord(u' ')
|
|
- for x in map(ord, _whitespace):
|
|
- unicode_whitespace_trans[x] = uspace
|
|
-
|
|
- # This funky little regex is just the trick for splitting
|
|
- # text up into word-wrappable chunks. E.g.
|
|
- # "Hello there -- you goof-ball, use the -b option!"
|
|
- # splits into
|
|
- # Hello/ /there/ /--/ /you/ /goof-/ball,/ /use/ /the/ /-b/ /option!
|
|
- # (after stripping out empty strings).
|
|
- wordsep_re = re.compile(r'(\s+|' # any whitespace
|
|
- r'-*\w{2,}-(?=\w{2,})|' # hyphenated words
|
|
- r'(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))') # em-dash
|
|
-
|
|
- # XXX will there be a locale-or-charset-aware version of
|
|
- # string.lowercase in 2.3?
|
|
- sentence_end_re = re.compile(r'[%s]' # lowercase letter
|
|
- r'[\.\!\?]' # sentence-ending punct.
|
|
- r'[\"\']?' # optional end-of-quote
|
|
- % string.lowercase)
|
|
-
|
|
-
|
|
- def __init__(self,
|
|
- width=70,
|
|
- initial_indent="",
|
|
- subsequent_indent="",
|
|
- expand_tabs=True,
|
|
- replace_whitespace=True,
|
|
- fix_sentence_endings=False,
|
|
- break_long_words=True):
|
|
- self.width = width
|
|
- self.initial_indent = initial_indent
|
|
- self.subsequent_indent = subsequent_indent
|
|
- self.expand_tabs = expand_tabs
|
|
- self.replace_whitespace = replace_whitespace
|
|
- self.fix_sentence_endings = fix_sentence_endings
|
|
- self.break_long_words = break_long_words
|
|
-
|
|
-
|
|
- # -- Private methods -----------------------------------------------
|
|
- # (possibly useful for subclasses to override)
|
|
-
|
|
- def _munge_whitespace(self, text):
|
|
- """_munge_whitespace(text : string) -> string
|
|
-
|
|
- Munge whitespace in text: expand tabs and convert all other
|
|
- whitespace characters to spaces. Eg. " foo\tbar\n\nbaz"
|
|
- becomes " foo bar baz".
|
|
- """
|
|
- if self.expand_tabs:
|
|
- text = text.expandtabs()
|
|
- if self.replace_whitespace:
|
|
- if isinstance(text, str):
|
|
- text = text.translate(self.whitespace_trans)
|
|
- elif isinstance(text, unicode):
|
|
- text = text.translate(self.unicode_whitespace_trans)
|
|
- return text
|
|
-
|
|
-
|
|
- def _split(self, text):
|
|
- """_split(text : string) -> [string]
|
|
-
|
|
- Split the text to wrap into indivisible chunks. Chunks are
|
|
- not quite the same as words; see wrap_chunks() for full
|
|
- details. As an example, the text
|
|
- Look, goof-ball -- use the -b option!
|
|
- breaks into the following chunks:
|
|
- 'Look,', ' ', 'goof-', 'ball', ' ', '--', ' ',
|
|
- 'use', ' ', 'the', ' ', '-b', ' ', 'option!'
|
|
- """
|
|
- chunks = self.wordsep_re.split(text)
|
|
- chunks = filter(None, chunks)
|
|
- return chunks
|
|
-
|
|
- def _fix_sentence_endings(self, chunks):
|
|
- """_fix_sentence_endings(chunks : [string])
|
|
-
|
|
- Correct for sentence endings buried in 'chunks'. Eg. when the
|
|
- original text contains "... foo.\nBar ...", munge_whitespace()
|
|
- and split() will convert that to [..., "foo.", " ", "Bar", ...]
|
|
- which has one too few spaces; this method simply changes the one
|
|
- space to two.
|
|
- """
|
|
- i = 0
|
|
- pat = self.sentence_end_re
|
|
- while i < len(chunks)-1:
|
|
- if chunks[i+1] == " " and pat.search(chunks[i]):
|
|
- chunks[i+1] = " "
|
|
- i += 2
|
|
- else:
|
|
- i += 1
|
|
-
|
|
- def _handle_long_word(self, chunks, cur_line, cur_len, width):
|
|
- """_handle_long_word(chunks : [string],
|
|
- cur_line : [string],
|
|
- cur_len : int, width : int)
|
|
-
|
|
- Handle a chunk of text (most likely a word, not whitespace) that
|
|
- is too long to fit in any line.
|
|
- """
|
|
- space_left = max(width - cur_len, 1)
|
|
-
|
|
- # If we're allowed to break long words, then do so: put as much
|
|
- # of the next chunk onto the current line as will fit.
|
|
- if self.break_long_words:
|
|
- cur_line.append(chunks[0][0:space_left])
|
|
- chunks[0] = chunks[0][space_left:]
|
|
-
|
|
- # Otherwise, we have to preserve the long word intact. Only add
|
|
- # it to the current line if there's nothing already there --
|
|
- # that minimizes how much we violate the width constraint.
|
|
- elif not cur_line:
|
|
- cur_line.append(chunks.pop(0))
|
|
-
|
|
- # If we're not allowed to break long words, and there's already
|
|
- # text on the current line, do nothing. Next time through the
|
|
- # main loop of _wrap_chunks(), we'll wind up here again, but
|
|
- # cur_len will be zero, so the next line will be entirely
|
|
- # devoted to the long word that we can't handle right now.
|
|
-
|
|
- def _wrap_chunks(self, chunks):
|
|
- """_wrap_chunks(chunks : [string]) -> [string]
|
|
-
|
|
- Wrap a sequence of text chunks and return a list of lines of
|
|
- length 'self.width' or less. (If 'break_long_words' is false,
|
|
- some lines may be longer than this.) Chunks correspond roughly
|
|
- to words and the whitespace between them: each chunk is
|
|
- indivisible (modulo 'break_long_words'), but a line break can
|
|
- come between any two chunks. Chunks should not have internal
|
|
- whitespace; ie. a chunk is either all whitespace or a "word".
|
|
- Whitespace chunks will be removed from the beginning and end of
|
|
- lines, but apart from that whitespace is preserved.
|
|
- """
|
|
- lines = []
|
|
- if self.width <= 0:
|
|
- raise ValueError("invalid width %r (must be > 0)" % self.width)
|
|
-
|
|
- while chunks:
|
|
-
|
|
- # Start the list of chunks that will make up the current line.
|
|
- # cur_len is just the length of all the chunks in cur_line.
|
|
- cur_line = []
|
|
- cur_len = 0
|
|
-
|
|
- # Figure out which static string will prefix this line.
|
|
- if lines:
|
|
- indent = self.subsequent_indent
|
|
- else:
|
|
- indent = self.initial_indent
|
|
-
|
|
- # Maximum width for this line.
|
|
- width = self.width - len(indent)
|
|
-
|
|
- # First chunk on line is whitespace -- drop it, unless this
|
|
- # is the very beginning of the text (ie. no lines started yet).
|
|
- if chunks[0].strip() == '' and lines:
|
|
- del chunks[0]
|
|
-
|
|
- while chunks:
|
|
- l = len(chunks[0])
|
|
-
|
|
- # Can at least squeeze this chunk onto the current line.
|
|
- if cur_len + l <= width:
|
|
- cur_line.append(chunks.pop(0))
|
|
- cur_len += l
|
|
-
|
|
- # Nope, this line is full.
|
|
- else:
|
|
- break
|
|
-
|
|
- # The current line is full, and the next chunk is too big to
|
|
- # fit on *any* line (not just this one).
|
|
- if chunks and len(chunks[0]) > width:
|
|
- self._handle_long_word(chunks, cur_line, cur_len, width)
|
|
-
|
|
- # If the last chunk on this line is all whitespace, drop it.
|
|
- if cur_line and cur_line[-1].strip() == '':
|
|
- del cur_line[-1]
|
|
-
|
|
- # Convert current line back to a string and store it in list
|
|
- # of all lines (return value).
|
|
- if cur_line:
|
|
- lines.append(indent + ''.join(cur_line))
|
|
-
|
|
- return lines
|
|
-
|
|
-
|
|
- # -- Public interface ----------------------------------------------
|
|
-
|
|
- def wrap(self, text):
|
|
- """wrap(text : string) -> [string]
|
|
-
|
|
- Reformat the single paragraph in 'text' so it fits in lines of
|
|
- no more than 'self.width' columns, and return a list of wrapped
|
|
- lines. Tabs in 'text' are expanded with string.expandtabs(),
|
|
- and all other whitespace characters (including newline) are
|
|
- converted to space.
|
|
- """
|
|
- text = self._munge_whitespace(text)
|
|
- indent = self.initial_indent
|
|
- chunks = self._split(text)
|
|
- if self.fix_sentence_endings:
|
|
- self._fix_sentence_endings(chunks)
|
|
- return self._wrap_chunks(chunks)
|
|
-
|
|
- def fill(self, text):
|
|
- """fill(text : string) -> string
|
|
-
|
|
- Reformat the single paragraph in 'text' to fit in lines of no
|
|
- more than 'self.width' columns, and return a new string
|
|
- containing the entire wrapped paragraph.
|
|
- """
|
|
- return "\n".join(self.wrap(text))
|
|
-
|
|
-
|
|
-# -- Convenience interface ---------------------------------------------
|
|
-
|
|
-def wrap(text, width=70, **kwargs):
|
|
- """Wrap a single paragraph of text, returning a list of wrapped lines.
|
|
-
|
|
- Reformat the single paragraph in 'text' so it fits in lines of no
|
|
- more than 'width' columns, and return a list of wrapped lines. By
|
|
- default, tabs in 'text' are expanded with string.expandtabs(), and
|
|
- all other whitespace characters (including newline) are converted to
|
|
- space. See TextWrapper class for available keyword args to customize
|
|
- wrapping behaviour.
|
|
- """
|
|
- w = TextWrapper(width=width, **kwargs)
|
|
- return w.wrap(text)
|
|
-
|
|
-def fill(text, width=70, **kwargs):
|
|
- """Fill a single paragraph of text, returning a new string.
|
|
-
|
|
- Reformat the single paragraph in 'text' to fit in lines of no more
|
|
- than 'width' columns, and return a new string containing the entire
|
|
- wrapped paragraph. As with wrap(), tabs are expanded and other
|
|
- whitespace characters converted to space. See TextWrapper class for
|
|
- available keyword args to customize wrapping behaviour.
|
|
- """
|
|
- w = TextWrapper(width=width, **kwargs)
|
|
- return w.fill(text)
|
|
-
|
|
-
|
|
-# -- Loosely related functionality -------------------------------------
|
|
-
|
|
-def dedent(text):
|
|
- """dedent(text : string) -> string
|
|
-
|
|
- Remove any whitespace than can be uniformly removed from the left
|
|
- of every line in `text`.
|
|
-
|
|
- This can be used e.g. to make triple-quoted strings line up with
|
|
- the left edge of screen/whatever, while still presenting it in the
|
|
- source code in indented form.
|
|
-
|
|
- For example:
|
|
-
|
|
- def test():
|
|
- # end first line with \ to avoid the empty line!
|
|
- s = '''\
|
|
- hello
|
|
- world
|
|
- '''
|
|
- print repr(s) # prints ' hello\n world\n '
|
|
- print repr(dedent(s)) # prints 'hello\n world\n'
|
|
- """
|
|
- lines = text.expandtabs().split('\n')
|
|
- margin = None
|
|
- for line in lines:
|
|
- content = line.lstrip()
|
|
- if not content:
|
|
- continue
|
|
- indent = len(line) - len(content)
|
|
- if margin is None:
|
|
- margin = indent
|
|
- else:
|
|
- margin = min(margin, indent)
|
|
-
|
|
- if margin is not None and margin > 0:
|
|
- for i in range(len(lines)):
|
|
- lines[i] = lines[i][margin:]
|
|
-
|
|
- return '\n'.join(lines)
|
|
diff --git a/koan/utils.py b/koan/utils.py
|
|
index 475bcd0..4a3f547 100644
|
|
--- a/koan/utils.py
|
|
+++ b/koan/utils.py
|
|
@@ -21,29 +21,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
02110-1301 USA
|
|
"""
|
|
|
|
-import random
|
|
import os
|
|
import traceback
|
|
-import tempfile
|
|
-ANCIENT_PYTHON = 0
|
|
-try:
|
|
- try:
|
|
- import subprocess as sub_process
|
|
- except:
|
|
- import sub_process
|
|
- import urllib2
|
|
-except:
|
|
- ANCIENT_PYTHON = 1
|
|
-import time
|
|
-import shutil
|
|
-import errno
|
|
-import re
|
|
import sys
|
|
+import subprocess as sub_process
|
|
+import urllib2
|
|
import xmlrpclib
|
|
import string
|
|
-import re
|
|
-import glob
|
|
-import socket
|
|
import shutil
|
|
import tempfile
|
|
import urlgrabber
|
|
@@ -131,13 +115,6 @@ def urlread(url):
|
|
fd.close()
|
|
return data
|
|
except:
|
|
- if ANCIENT_PYTHON:
|
|
- # this logic is to support python 1.5 and EL 2
|
|
- import urllib
|
|
- fd = urllib.urlopen(url)
|
|
- data = fd.read()
|
|
- fd.close()
|
|
- return data
|
|
traceback.print_exc()
|
|
raise InfoException, "Couldn't download: %s" % url
|
|
elif url[0:4] == "file":
|
|
@@ -167,12 +144,7 @@ def subprocess_call(cmd,ignore_rc=0):
|
|
Wrapper around subprocess.call(...)
|
|
"""
|
|
print "- %s" % cmd
|
|
- if not ANCIENT_PYTHON:
|
|
- rc = sub_process.call(cmd)
|
|
- else:
|
|
- cmd = string.join(cmd, " ")
|
|
- print "cmdstr=(%s)" % cmd
|
|
- rc = os.system(cmd)
|
|
+ rc = sub_process.call(cmd)
|
|
if rc != 0 and not ignore_rc:
|
|
raise InfoException, "command failed (%s)" % rc
|
|
return rc
|
|
@@ -184,15 +156,10 @@ def subprocess_get_response(cmd, ignore_rc=False):
|
|
print "- %s" % cmd
|
|
rc = 0
|
|
result = ""
|
|
- if not ANCIENT_PYTHON:
|
|
- p = sub_process.Popen(cmd, stdout=sub_process.PIPE, stderr=sub_process.STDOUT)
|
|
- result, stderr = p.communicate()
|
|
- result = result.strip()
|
|
- rc = p.poll()
|
|
- else:
|
|
- cmd = string.join(cmd, " ")
|
|
- print "cmdstr=(%s)" % cmd
|
|
- rc = os.system(cmd)
|
|
+ p = sub_process.Popen(cmd, stdout=sub_process.PIPE, stderr=sub_process.STDOUT)
|
|
+ result, stderr = p.communicate()
|
|
+ result = result.strip()
|
|
+ rc = p.poll()
|
|
if not ignore_rc and rc != 0:
|
|
raise InfoException, "command failed (%s)" % rc
|
|
return rc, result
|
|
@@ -347,9 +314,6 @@ def os_release():
|
|
This code is borrowed from Cobbler and really shouldn't be repeated.
|
|
"""
|
|
|
|
- if ANCIENT_PYTHON:
|
|
- return ("unknown", 0)
|
|
-
|
|
if check_dist() == "redhat":
|
|
fh = open("/etc/redhat-release")
|
|
data = fh.read().lower()
|
|
diff --git a/koan/vmwcreate.py b/koan/vmwcreate.py
|
|
index 1bde891..3fda926 100755
|
|
--- a/koan/vmwcreate.py
|
|
+++ b/koan/vmwcreate.py
|
|
@@ -21,12 +21,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
"""
|
|
|
|
|
|
-import os, sys, time, stat
|
|
-import tempfile
|
|
+import os
|
|
import random
|
|
-from optparse import OptionParser
|
|
-import errno
|
|
-import re
|
|
import virtinst
|
|
|
|
IMAGE_DIR = "/var/lib/vmware/images"
|
|
--
|
|
2.5.5
|
|
|