diff --git a/.gitignore b/.gitignore index 07a356f..0a3cb4a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ ipython-0.10.tar.gz +/ipython-0.10.1.tar.gz diff --git a/ipython-unbundle-external-module.patch b/ipython-unbundle-external-module.patch index 028a7d0..6fc8011 100644 --- a/ipython-unbundle-external-module.patch +++ b/ipython-unbundle-external-module.patch @@ -1,2224 +1,3 @@ -Index: ipython-0.10/IPython/external/argparse/_argparse.py -=================================================================== ---- /dev/null -+++ ipython-0.10/IPython/external/argparse/_argparse.py -@@ -0,0 +1,2216 @@ -+# -*- coding: utf-8 -*- -+ -+# Copyright © 2006-2009 Steven J. Bethard . -+# -+# Licensed under the Apache License, Version 2.0 (the "License"); you may not -+# use this file except in compliance with the License. You may obtain a copy -+# of the License at -+# -+# http://www.apache.org/licenses/LICENSE-2.0 -+# -+# Unless required by applicable law or agreed to in writing, software -+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -+# License for the specific language governing permissions and limitations -+# under the License. -+ -+"""Command-line parsing library -+ -+This module is an optparse-inspired command-line parsing library that: -+ -+ - handles both optional and positional arguments -+ - produces highly informative usage messages -+ - supports parsers that dispatch to sub-parsers -+ -+The following is a simple usage example that sums integers from the -+command-line and writes the result to a file:: -+ -+ parser = argparse.ArgumentParser( -+ description='sum the integers at the command line') -+ parser.add_argument( -+ 'integers', metavar='int', nargs='+', type=int, -+ help='an integer to be summed') -+ parser.add_argument( -+ '--log', default=sys.stdout, type=argparse.FileType('w'), -+ help='the file where the sum should be written') -+ args = parser.parse_args() -+ args.log.write('%s' % sum(args.integers)) -+ args.log.close() -+ -+The module contains the following public classes: -+ -+ - ArgumentParser -- The main entry point for command-line parsing. As the -+ example above shows, the add_argument() method is used to populate -+ the parser with actions for optional and positional arguments. Then -+ the parse_args() method is invoked to convert the args at the -+ command-line into an object with attributes. -+ -+ - ArgumentError -- The exception raised by ArgumentParser objects when -+ there are errors with the parser's actions. Errors raised while -+ parsing the command-line are caught by ArgumentParser and emitted -+ as command-line messages. -+ -+ - FileType -- A factory for defining types of files to be created. As the -+ example above shows, instances of FileType are typically passed as -+ the type= argument of add_argument() calls. -+ -+ - Action -- The base class for parser actions. Typically actions are -+ selected by passing strings like 'store_true' or 'append_const' to -+ the action= argument of add_argument(). However, for greater -+ customization of ArgumentParser actions, subclasses of Action may -+ be defined and passed as the action= argument. -+ -+ - HelpFormatter, RawDescriptionHelpFormatter, RawTextHelpFormatter, -+ ArgumentDefaultsHelpFormatter -- Formatter classes which -+ may be passed as the formatter_class= argument to the -+ ArgumentParser constructor. HelpFormatter is the default, -+ RawDescriptionHelpFormatter and RawTextHelpFormatter tell the parser -+ not to change the formatting for help text, and -+ ArgumentDefaultsHelpFormatter adds information about argument defaults -+ to the help. -+ -+All other classes in this module are considered implementation details. -+(Also note that HelpFormatter and RawDescriptionHelpFormatter are only -+considered public as object names -- the API of the formatter objects is -+still considered an implementation detail.) -+""" -+ -+__version__ = '1.0' -+__all__ = [ -+ 'ArgumentParser', -+ 'ArgumentError', -+ 'Namespace', -+ 'Action', -+ 'FileType', -+ 'HelpFormatter', -+ 'RawDescriptionHelpFormatter', -+ 'RawTextHelpFormatter', -+ 'ArgumentDefaultsHelpFormatter', -+] -+ -+ -+import copy as _copy -+import os as _os -+import re as _re -+import sys as _sys -+import textwrap as _textwrap -+ -+from gettext import gettext as _ -+ -+try: -+ _set = set -+except NameError: -+ from sets import Set as _set -+ -+try: -+ _basestring = basestring -+except NameError: -+ _basestring = str -+ -+try: -+ _sorted = sorted -+except NameError: -+ -+ def _sorted(iterable, reverse=False): -+ result = list(iterable) -+ result.sort() -+ if reverse: -+ result.reverse() -+ return result -+ -+ -+SUPPRESS = '==SUPPRESS==' -+ -+OPTIONAL = '?' -+ZERO_OR_MORE = '*' -+ONE_OR_MORE = '+' -+PARSER = '==PARSER==' -+ -+# ============================= -+# Utility functions and classes -+# ============================= -+ -+class _AttributeHolder(object): -+ """Abstract base class that provides __repr__. -+ -+ The __repr__ method returns a string in the format:: -+ ClassName(attr=name, attr=name, ...) -+ The attributes are determined either by a class-level attribute, -+ '_kwarg_names', or by inspecting the instance __dict__. -+ """ -+ -+ def __repr__(self): -+ type_name = type(self).__name__ -+ arg_strings = [] -+ for arg in self._get_args(): -+ arg_strings.append(repr(arg)) -+ for name, value in self._get_kwargs(): -+ arg_strings.append('%s=%r' % (name, value)) -+ return '%s(%s)' % (type_name, ', '.join(arg_strings)) -+ -+ def _get_kwargs(self): -+ return _sorted(self.__dict__.items()) -+ -+ def _get_args(self): -+ return [] -+ -+ -+def _ensure_value(namespace, name, value): -+ if getattr(namespace, name, None) is None: -+ setattr(namespace, name, value) -+ return getattr(namespace, name) -+ -+ -+# =============== -+# Formatting Help -+# =============== -+ -+class HelpFormatter(object): -+ """Formatter for generating usage messages and argument help strings. -+ -+ Only the name of this class is considered a public API. All the methods -+ provided by the class are considered an implementation detail. -+ """ -+ -+ def __init__(self, -+ prog, -+ indent_increment=2, -+ max_help_position=24, -+ width=None): -+ -+ # default setting for width -+ if width is None: -+ try: -+ width = int(_os.environ['COLUMNS']) -+ except (KeyError, ValueError): -+ width = 80 -+ width -= 2 -+ -+ self._prog = prog -+ self._indent_increment = indent_increment -+ self._max_help_position = max_help_position -+ self._width = width -+ -+ self._current_indent = 0 -+ self._level = 0 -+ self._action_max_length = 0 -+ -+ self._root_section = self._Section(self, None) -+ self._current_section = self._root_section -+ -+ self._whitespace_matcher = _re.compile(r'\s+') -+ self._long_break_matcher = _re.compile(r'\n\n\n+') -+ -+ # =============================== -+ # Section and indentation methods -+ # =============================== -+ 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 -+ -+ class _Section(object): -+ -+ def __init__(self, formatter, parent, heading=None): -+ self.formatter = formatter -+ self.parent = parent -+ self.heading = heading -+ self.items = [] -+ -+ def format_help(self): -+ # format the indented section -+ if self.parent is not None: -+ self.formatter._indent() -+ join = self.formatter._join_parts -+ for func, args in self.items: -+ func(*args) -+ item_help = join([func(*args) for func, args in self.items]) -+ if self.parent is not None: -+ self.formatter._dedent() -+ -+ # return nothing if the section was empty -+ if not item_help: -+ return '' -+ -+ # add the heading if the section was non-empty -+ if self.heading is not SUPPRESS and self.heading is not None: -+ current_indent = self.formatter._current_indent -+ heading = '%*s%s:\n' % (current_indent, '', self.heading) -+ else: -+ heading = '' -+ -+ # join the section-initial newline, the heading and the help -+ return join(['\n', heading, item_help, '\n']) -+ -+ def _add_item(self, func, args): -+ self._current_section.items.append((func, args)) -+ -+ # ======================== -+ # Message building methods -+ # ======================== -+ def start_section(self, heading): -+ self._indent() -+ section = self._Section(self, self._current_section, heading) -+ self._add_item(section.format_help, []) -+ self._current_section = section -+ -+ def end_section(self): -+ self._current_section = self._current_section.parent -+ self._dedent() -+ -+ def add_text(self, text): -+ if text is not SUPPRESS and text is not None: -+ self._add_item(self._format_text, [text]) -+ -+ def add_usage(self, usage, actions, groups, prefix=None): -+ if usage is not SUPPRESS: -+ args = usage, actions, groups, prefix -+ self._add_item(self._format_usage, args) -+ -+ def add_argument(self, action): -+ if action.help is not SUPPRESS: -+ -+ # find all invocations -+ get_invocation = self._format_action_invocation -+ invocations = [get_invocation(action)] -+ for subaction in self._iter_indented_subactions(action): -+ invocations.append(get_invocation(subaction)) -+ -+ # update the maximum item length -+ invocation_length = max([len(s) for s in invocations]) -+ action_length = invocation_length + self._current_indent -+ self._action_max_length = max(self._action_max_length, -+ action_length) -+ -+ # add the item to the list -+ self._add_item(self._format_action, [action]) -+ -+ def add_arguments(self, actions): -+ for action in actions: -+ self.add_argument(action) -+ -+ # ======================= -+ # Help-formatting methods -+ # ======================= -+ def format_help(self): -+ help = self._root_section.format_help() % dict(prog=self._prog) -+ if help: -+ help = self._long_break_matcher.sub('\n\n', help) -+ help = help.strip('\n') + '\n' -+ return help -+ -+ def _join_parts(self, part_strings): -+ return ''.join([part -+ for part in part_strings -+ if part and part is not SUPPRESS]) -+ -+ def _format_usage(self, usage, actions, groups, prefix): -+ if prefix is None: -+ prefix = _('usage: ') -+ -+ # if no optionals or positionals are available, usage is just prog -+ if usage is None and not actions: -+ usage = '%(prog)s' -+ -+ # if optionals and positionals are available, calculate usage -+ elif usage is None: -+ usage = '%(prog)s' % dict(prog=self._prog) -+ -+ # split optionals from positionals -+ optionals = [] -+ positionals = [] -+ for action in actions: -+ if action.option_strings: -+ optionals.append(action) -+ else: -+ positionals.append(action) -+ -+ # determine width of "usage: PROG" and width of text -+ prefix_width = len(prefix) + len(usage) + 1 -+ prefix_indent = self._current_indent + prefix_width -+ text_width = self._width - self._current_indent -+ -+ # put them on one line if they're short enough -+ format = self._format_actions_usage -+ action_usage = format(optionals + positionals, groups) -+ if prefix_width + len(action_usage) + 1 < text_width: -+ usage = '%s %s' % (usage, action_usage) -+ -+ # if they're long, wrap optionals and positionals individually -+ else: -+ optional_usage = format(optionals, groups) -+ positional_usage = format(positionals, groups) -+ indent = ' ' * prefix_indent -+ -+ # usage is made of PROG, optionals and positionals -+ parts = [usage, ' '] -+ -+ # options always get added right after PROG -+ if optional_usage: -+ parts.append(_textwrap.fill( -+ optional_usage, text_width, -+ initial_indent=indent, -+ subsequent_indent=indent).lstrip()) -+ -+ # if there were options, put arguments on the next line -+ # otherwise, start them right after PROG -+ if positional_usage: -+ part = _textwrap.fill( -+ positional_usage, text_width, -+ initial_indent=indent, -+ subsequent_indent=indent).lstrip() -+ if optional_usage: -+ part = '\n' + indent + part -+ parts.append(part) -+ usage = ''.join(parts) -+ -+ # prefix with 'usage:' -+ return '%s%s\n\n' % (prefix, usage) -+ -+ def _format_actions_usage(self, actions, groups): -+ # find group indices and identify actions in groups -+ group_actions = _set() -+ inserts = {} -+ for group in groups: -+ try: -+ start = actions.index(group._group_actions[0]) -+ except ValueError: -+ continue -+ else: -+ end = start + len(group._group_actions) -+ if actions[start:end] == group._group_actions: -+ for action in group._group_actions: -+ group_actions.add(action) -+ if not group.required: -+ inserts[start] = '[' -+ inserts[end] = ']' -+ else: -+ inserts[start] = '(' -+ inserts[end] = ')' -+ for i in range(start + 1, end): -+ inserts[i] = '|' -+ -+ # collect all actions format strings -+ parts = [] -+ for i, action in enumerate(actions): -+ -+ # suppressed arguments are marked with None -+ # remove | separators for suppressed arguments -+ if action.help is SUPPRESS: -+ parts.append(None) -+ if inserts.get(i) == '|': -+ inserts.pop(i) -+ elif inserts.get(i + 1) == '|': -+ inserts.pop(i + 1) -+ -+ # produce all arg strings -+ elif not action.option_strings: -+ part = self._format_args(action, action.dest) -+ -+ # if it's in a group, strip the outer [] -+ if action in group_actions: -+ if part[0] == '[' and part[-1] == ']': -+ part = part[1:-1] -+ -+ # add the action string to the list -+ parts.append(part) -+ -+ # produce the first way to invoke the option in brackets -+ else: -+ option_string = action.option_strings[0] -+ -+ # if the Optional doesn't take a value, format is: -+ # -s or --long -+ if action.nargs == 0: -+ part = '%s' % option_string -+ -+ # if the Optional takes a value, format is: -+ # -s ARGS or --long ARGS -+ else: -+ default = action.dest.upper() -+ args_string = self._format_args(action, default) -+ part = '%s %s' % (option_string, args_string) -+ -+ # make it look optional if it's not required or in a group -+ if not action.required and action not in group_actions: -+ part = '[%s]' % part -+ -+ # add the action string to the list -+ parts.append(part) -+ -+ # insert things at the necessary indices -+ for i in _sorted(inserts, reverse=True): -+ parts[i:i] = [inserts[i]] -+ -+ # join all the action items with spaces -+ text = ' '.join([item for item in parts if item is not None]) -+ -+ # clean up separators for mutually exclusive groups -+ open = r'[\[(]' -+ close = r'[\])]' -+ text = _re.sub(r'(%s) ' % open, r'\1', text) -+ text = _re.sub(r' (%s)' % close, r'\1', text) -+ text = _re.sub(r'%s *%s' % (open, close), r'', text) -+ text = _re.sub(r'\(([^|]*)\)', r'\1', text) -+ text = text.strip() -+ -+ # return the text -+ return text -+ -+ def _format_text(self, text): -+ text_width = self._width - self._current_indent -+ indent = ' ' * self._current_indent -+ return self._fill_text(text, text_width, indent) + '\n\n' -+ -+ def _format_action(self, action): -+ # determine the required width and the entry label -+ help_position = min(self._action_max_length + 2, -+ self._max_help_position) -+ help_width = self._width - help_position -+ action_width = help_position - self._current_indent - 2 -+ action_header = self._format_action_invocation(action) -+ -+ # ho nelp; start on same line and add a final newline -+ if not action.help: -+ tup = self._current_indent, '', action_header -+ action_header = '%*s%s\n' % tup -+ -+ # short action name; start on the same line and pad two spaces -+ elif len(action_header) <= action_width: -+ tup = self._current_indent, '', action_width, action_header -+ action_header = '%*s%-*s ' % tup -+ indent_first = 0 -+ -+ # long action name; start on the next line -+ else: -+ tup = self._current_indent, '', action_header -+ action_header = '%*s%s\n' % tup -+ indent_first = help_position -+ -+ # collect the pieces of the action help -+ parts = [action_header] -+ -+ # if there was help for the action, add lines of help text -+ if action.help: -+ help_text = self._expand_help(action) -+ help_lines = self._split_lines(help_text, help_width) -+ parts.append('%*s%s\n' % (indent_first, '', help_lines[0])) -+ for line in help_lines[1:]: -+ parts.append('%*s%s\n' % (help_position, '', line)) -+ -+ # or add a newline if the description doesn't end with one -+ elif not action_header.endswith('\n'): -+ parts.append('\n') -+ -+ # if there are any sub-actions, add their help as well -+ for subaction in self._iter_indented_subactions(action): -+ parts.append(self._format_action(subaction)) -+ -+ # return a single string -+ return self._join_parts(parts) -+ -+ def _format_action_invocation(self, action): -+ if not action.option_strings: -+ metavar, = self._metavar_formatter(action, action.dest)(1) -+ return metavar -+ -+ else: -+ parts = [] -+ -+ # if the Optional doesn't take a value, format is: -+ # -s, --long -+ if action.nargs == 0: -+ parts.extend(action.option_strings) -+ -+ # if the Optional takes a value, format is: -+ # -s ARGS, --long ARGS -+ else: -+ default = action.dest.upper() -+ args_string = self._format_args(action, default) -+ for option_string in action.option_strings: -+ parts.append('%s %s' % (option_string, args_string)) -+ -+ return ', '.join(parts) -+ -+ def _metavar_formatter(self, action, default_metavar): -+ if action.metavar is not None: -+ result = action.metavar -+ elif action.choices is not None: -+ choice_strs = [str(choice) for choice in action.choices] -+ result = '{%s}' % ','.join(choice_strs) -+ else: -+ result = default_metavar -+ -+ def format(tuple_size): -+ if isinstance(result, tuple): -+ return result -+ else: -+ return (result, ) * tuple_size -+ return format -+ -+ def _format_args(self, action, default_metavar): -+ get_metavar = self._metavar_formatter(action, default_metavar) -+ if action.nargs is None: -+ result = '%s' % get_metavar(1) -+ elif action.nargs == OPTIONAL: -+ result = '[%s]' % get_metavar(1) -+ elif action.nargs == ZERO_OR_MORE: -+ result = '[%s [%s ...]]' % get_metavar(2) -+ elif action.nargs == ONE_OR_MORE: -+ result = '%s [%s ...]' % get_metavar(2) -+ elif action.nargs is PARSER: -+ result = '%s ...' % get_metavar(1) -+ else: -+ formats = ['%s' for _ in range(action.nargs)] -+ result = ' '.join(formats) % get_metavar(action.nargs) -+ return result -+ -+ def _expand_help(self, action): -+ params = dict(vars(action), prog=self._prog) -+ for name in list(params): -+ if params[name] is SUPPRESS: -+ del params[name] -+ if params.get('choices') is not None: -+ choices_str = ', '.join([str(c) for c in params['choices']]) -+ params['choices'] = choices_str -+ return self._get_help_string(action) % params -+ -+ def _iter_indented_subactions(self, action): -+ try: -+ get_subactions = action._get_subactions -+ except AttributeError: -+ pass -+ else: -+ self._indent() -+ for subaction in get_subactions(): -+ yield subaction -+ self._dedent() -+ -+ def _split_lines(self, text, width): -+ text = self._whitespace_matcher.sub(' ', text).strip() -+ return _textwrap.wrap(text, width) -+ -+ def _fill_text(self, text, width, indent): -+ text = self._whitespace_matcher.sub(' ', text).strip() -+ return _textwrap.fill(text, width, initial_indent=indent, -+ subsequent_indent=indent) -+ -+ def _get_help_string(self, action): -+ return action.help -+ -+ -+class RawDescriptionHelpFormatter(HelpFormatter): -+ """Help message formatter which retains any formatting in descriptions. -+ -+ Only the name of this class is considered a public API. All the methods -+ provided by the class are considered an implementation detail. -+ """ -+ -+ def _fill_text(self, text, width, indent): -+ return ''.join([indent + line for line in text.splitlines(True)]) -+ -+ -+class RawTextHelpFormatter(RawDescriptionHelpFormatter): -+ """Help message formatter which retains formatting of all help text. -+ -+ Only the name of this class is considered a public API. All the methods -+ provided by the class are considered an implementation detail. -+ """ -+ -+ def _split_lines(self, text, width): -+ return text.splitlines() -+ -+ -+class ArgumentDefaultsHelpFormatter(HelpFormatter): -+ """Help message formatter which adds default values to argument help. -+ -+ Only the name of this class is considered a public API. All the methods -+ provided by the class are considered an implementation detail. -+ """ -+ -+ def _get_help_string(self, action): -+ help = action.help -+ if '%(default)' not in action.help: -+ if action.default is not SUPPRESS: -+ defaulting_nargs = [OPTIONAL, ZERO_OR_MORE] -+ if action.option_strings or action.nargs in defaulting_nargs: -+ help += ' (default: %(default)s)' -+ return help -+ -+ -+# ===================== -+# Options and Arguments -+# ===================== -+ -+def _get_action_name(argument): -+ if argument is None: -+ return None -+ elif argument.option_strings: -+ return '/'.join(argument.option_strings) -+ elif argument.metavar not in (None, SUPPRESS): -+ return argument.metavar -+ elif argument.dest not in (None, SUPPRESS): -+ return argument.dest -+ else: -+ return None -+ -+ -+class ArgumentError(Exception): -+ """An error from creating or using an argument (optional or positional). -+ -+ The string value of this exception is the message, augmented with -+ information about the argument that caused it. -+ """ -+ -+ def __init__(self, argument, message): -+ self.argument_name = _get_action_name(argument) -+ self.message = message -+ -+ def __str__(self): -+ if self.argument_name is None: -+ format = '%(message)s' -+ else: -+ format = 'argument %(argument_name)s: %(message)s' -+ return format % dict(message=self.message, -+ argument_name=self.argument_name) -+ -+# ============== -+# Action classes -+# ============== -+ -+class Action(_AttributeHolder): -+ """Information about how to convert command line strings to Python objects. -+ -+ Action objects are used by an ArgumentParser to represent the information -+ needed to parse a single argument from one or more strings from the -+ command line. The keyword arguments to the Action constructor are also -+ all attributes of Action instances. -+ -+ Keyword Arguments: -+ -+ - option_strings -- A list of command-line option strings which -+ should be associated with this action. -+ -+ - dest -- The name of the attribute to hold the created object(s) -+ -+ - nargs -- The number of command-line arguments that should be -+ consumed. By default, one argument will be consumed and a single -+ value will be produced. Other values include: -+ - N (an integer) consumes N arguments (and produces a list) -+ - '?' consumes zero or one arguments -+ - '*' consumes zero or more arguments (and produces a list) -+ - '+' consumes one or more arguments (and produces a list) -+ Note that the difference between the default and nargs=1 is that -+ with the default, a single value will be produced, while with -+ nargs=1, a list containing a single value will be produced. -+ -+ - const -- The value to be produced if the option is specified and the -+ option uses an action that takes no values. -+ -+ - default -- The value to be produced if the option is not specified. -+ -+ - type -- The type which the command-line arguments should be converted -+ to, should be one of 'string', 'int', 'float', 'complex' or a -+ callable object that accepts a single string argument. If None, -+ 'string' is assumed. -+ -+ - choices -- A container of values that should be allowed. If not None, -+ after a command-line argument has been converted to the appropriate -+ type, an exception will be raised if it is not a member of this -+ collection. -+ -+ - required -- True if the action must always be specified at the -+ command line. This is only meaningful for optional command-line -+ arguments. -+ -+ - help -- The help string describing the argument. -+ -+ - metavar -- The name to be used for the option's argument with the -+ help string. If None, the 'dest' value will be used as the name. -+ """ -+ -+ def __init__(self, -+ option_strings, -+ dest, -+ nargs=None, -+ const=None, -+ default=None, -+ type=None, -+ choices=None, -+ required=False, -+ help=None, -+ metavar=None): -+ self.option_strings = option_strings -+ self.dest = dest -+ self.nargs = nargs -+ self.const = const -+ self.default = default -+ self.type = type -+ self.choices = choices -+ self.required = required -+ self.help = help -+ self.metavar = metavar -+ -+ def _get_kwargs(self): -+ names = [ -+ 'option_strings', -+ 'dest', -+ 'nargs', -+ 'const', -+ 'default', -+ 'type', -+ 'choices', -+ 'help', -+ 'metavar', -+ ] -+ return [(name, getattr(self, name)) for name in names] -+ -+ def __call__(self, parser, namespace, values, option_string=None): -+ raise NotImplementedError(_('.__call__() not defined')) -+ -+ -+class _StoreAction(Action): -+ -+ def __init__(self, -+ option_strings, -+ dest, -+ nargs=None, -+ const=None, -+ default=None, -+ type=None, -+ choices=None, -+ required=False, -+ help=None, -+ metavar=None): -+ if nargs == 0: -+ raise ValueError('nargs must be > 0') -+ if const is not None and nargs != OPTIONAL: -+ raise ValueError('nargs must be %r to supply const' % OPTIONAL) -+ super(_StoreAction, self).__init__( -+ option_strings=option_strings, -+ dest=dest, -+ nargs=nargs, -+ const=const, -+ default=default, -+ type=type, -+ choices=choices, -+ required=required, -+ help=help, -+ metavar=metavar) -+ -+ def __call__(self, parser, namespace, values, option_string=None): -+ setattr(namespace, self.dest, values) -+ -+ -+class _StoreConstAction(Action): -+ -+ def __init__(self, -+ option_strings, -+ dest, -+ const, -+ default=None, -+ required=False, -+ help=None, -+ metavar=None): -+ super(_StoreConstAction, self).__init__( -+ option_strings=option_strings, -+ dest=dest, -+ nargs=0, -+ const=const, -+ default=default, -+ required=required, -+ help=help) -+ -+ def __call__(self, parser, namespace, values, option_string=None): -+ setattr(namespace, self.dest, self.const) -+ -+ -+class _StoreTrueAction(_StoreConstAction): -+ -+ def __init__(self, -+ option_strings, -+ dest, -+ default=False, -+ required=False, -+ help=None): -+ super(_StoreTrueAction, self).__init__( -+ option_strings=option_strings, -+ dest=dest, -+ const=True, -+ default=default, -+ required=required, -+ help=help) -+ -+ -+class _StoreFalseAction(_StoreConstAction): -+ -+ def __init__(self, -+ option_strings, -+ dest, -+ default=True, -+ required=False, -+ help=None): -+ super(_StoreFalseAction, self).__init__( -+ option_strings=option_strings, -+ dest=dest, -+ const=False, -+ default=default, -+ required=required, -+ help=help) -+ -+ -+class _AppendAction(Action): -+ -+ def __init__(self, -+ option_strings, -+ dest, -+ nargs=None, -+ const=None, -+ default=None, -+ type=None, -+ choices=None, -+ required=False, -+ help=None, -+ metavar=None): -+ if nargs == 0: -+ raise ValueError('nargs must be > 0') -+ if const is not None and nargs != OPTIONAL: -+ raise ValueError('nargs must be %r to supply const' % OPTIONAL) -+ super(_AppendAction, self).__init__( -+ option_strings=option_strings, -+ dest=dest, -+ nargs=nargs, -+ const=const, -+ default=default, -+ type=type, -+ choices=choices, -+ required=required, -+ help=help, -+ metavar=metavar) -+ -+ def __call__(self, parser, namespace, values, option_string=None): -+ items = _copy.copy(_ensure_value(namespace, self.dest, [])) -+ items.append(values) -+ setattr(namespace, self.dest, items) -+ -+ -+class _AppendConstAction(Action): -+ -+ def __init__(self, -+ option_strings, -+ dest, -+ const, -+ default=None, -+ required=False, -+ help=None, -+ metavar=None): -+ super(_AppendConstAction, self).__init__( -+ option_strings=option_strings, -+ dest=dest, -+ nargs=0, -+ const=const, -+ default=default, -+ required=required, -+ help=help, -+ metavar=metavar) -+ -+ def __call__(self, parser, namespace, values, option_string=None): -+ items = _copy.copy(_ensure_value(namespace, self.dest, [])) -+ items.append(self.const) -+ setattr(namespace, self.dest, items) -+ -+ -+class _CountAction(Action): -+ -+ def __init__(self, -+ option_strings, -+ dest, -+ default=None, -+ required=False, -+ help=None): -+ super(_CountAction, self).__init__( -+ option_strings=option_strings, -+ dest=dest, -+ nargs=0, -+ default=default, -+ required=required, -+ help=help) -+ -+ def __call__(self, parser, namespace, values, option_string=None): -+ new_count = _ensure_value(namespace, self.dest, 0) + 1 -+ setattr(namespace, self.dest, new_count) -+ -+ -+class _HelpAction(Action): -+ -+ def __init__(self, -+ option_strings, -+ dest=SUPPRESS, -+ default=SUPPRESS, -+ help=None): -+ super(_HelpAction, self).__init__( -+ option_strings=option_strings, -+ dest=dest, -+ default=default, -+ nargs=0, -+ help=help) -+ -+ def __call__(self, parser, namespace, values, option_string=None): -+ parser.print_help() -+ parser.exit() -+ -+ -+class _VersionAction(Action): -+ -+ def __init__(self, -+ option_strings, -+ dest=SUPPRESS, -+ default=SUPPRESS, -+ help=None): -+ super(_VersionAction, self).__init__( -+ option_strings=option_strings, -+ dest=dest, -+ default=default, -+ nargs=0, -+ help=help) -+ -+ def __call__(self, parser, namespace, values, option_string=None): -+ parser.print_version() -+ parser.exit() -+ -+ -+class _SubParsersAction(Action): -+ -+ class _ChoicesPseudoAction(Action): -+ -+ def __init__(self, name, help): -+ sup = super(_SubParsersAction._ChoicesPseudoAction, self) -+ sup.__init__(option_strings=[], dest=name, help=help) -+ -+ def __init__(self, -+ option_strings, -+ prog, -+ parser_class, -+ dest=SUPPRESS, -+ help=None, -+ metavar=None): -+ -+ self._prog_prefix = prog -+ self._parser_class = parser_class -+ self._name_parser_map = {} -+ self._choices_actions = [] -+ -+ super(_SubParsersAction, self).__init__( -+ option_strings=option_strings, -+ dest=dest, -+ nargs=PARSER, -+ choices=self._name_parser_map, -+ help=help, -+ metavar=metavar) -+ -+ def add_parser(self, name, **kwargs): -+ # set prog from the existing prefix -+ if kwargs.get('prog') is None: -+ kwargs['prog'] = '%s %s' % (self._prog_prefix, name) -+ -+ # create a pseudo-action to hold the choice help -+ if 'help' in kwargs: -+ help = kwargs.pop('help') -+ choice_action = self._ChoicesPseudoAction(name, help) -+ self._choices_actions.append(choice_action) -+ -+ # create the parser and add it to the map -+ parser = self._parser_class(**kwargs) -+ self._name_parser_map[name] = parser -+ return parser -+ -+ def _get_subactions(self): -+ return self._choices_actions -+ -+ def __call__(self, parser, namespace, values, option_string=None): -+ parser_name = values[0] -+ arg_strings = values[1:] -+ -+ # set the parser name if requested -+ if self.dest is not SUPPRESS: -+ setattr(namespace, self.dest, parser_name) -+ -+ # select the parser -+ try: -+ parser = self._name_parser_map[parser_name] -+ except KeyError: -+ tup = parser_name, ', '.join(self._name_parser_map) -+ msg = _('unknown parser %r (choices: %s)' % tup) -+ raise ArgumentError(self, msg) -+ -+ # parse all the remaining options into the namespace -+ parser.parse_args(arg_strings, namespace) -+ -+ -+# ============== -+# Type classes -+# ============== -+ -+class FileType(object): -+ """Factory for creating file object types -+ -+ Instances of FileType are typically passed as type= arguments to the -+ ArgumentParser add_argument() method. -+ -+ Keyword Arguments: -+ - mode -- A string indicating how the file is to be opened. Accepts the -+ same values as the builtin open() function. -+ - bufsize -- The file's desired buffer size. Accepts the same values as -+ the builtin open() function. -+ """ -+ -+ def __init__(self, mode='r', bufsize=None): -+ self._mode = mode -+ self._bufsize = bufsize -+ -+ def __call__(self, string): -+ # the special argument "-" means sys.std{in,out} -+ if string == '-': -+ if 'r' in self._mode: -+ return _sys.stdin -+ elif 'w' in self._mode: -+ return _sys.stdout -+ else: -+ msg = _('argument "-" with mode %r' % self._mode) -+ raise ValueError(msg) -+ -+ # all other arguments are used as file names -+ if self._bufsize: -+ return open(string, self._mode, self._bufsize) -+ else: -+ return open(string, self._mode) -+ -+ def __repr__(self): -+ args = [self._mode, self._bufsize] -+ args_str = ', '.join([repr(arg) for arg in args if arg is not None]) -+ return '%s(%s)' % (type(self).__name__, args_str) -+ -+# =========================== -+# Optional and Positional Parsing -+# =========================== -+ -+class Namespace(_AttributeHolder): -+ """Simple object for storing attributes. -+ -+ Implements equality by attribute names and values, and provides a simple -+ string representation. -+ """ -+ -+ def __init__(self, **kwargs): -+ for name in kwargs: -+ setattr(self, name, kwargs[name]) -+ -+ def __eq__(self, other): -+ return vars(self) == vars(other) -+ -+ def __ne__(self, other): -+ return not (self == other) -+ -+ -+class _ActionsContainer(object): -+ -+ def __init__(self, -+ description, -+ prefix_chars, -+ argument_default, -+ conflict_handler): -+ super(_ActionsContainer, self).__init__() -+ -+ self.description = description -+ self.argument_default = argument_default -+ self.prefix_chars = prefix_chars -+ self.conflict_handler = conflict_handler -+ -+ # set up registries -+ self._registries = {} -+ -+ # register actions -+ self.register('action', None, _StoreAction) -+ self.register('action', 'store', _StoreAction) -+ self.register('action', 'store_const', _StoreConstAction) -+ self.register('action', 'store_true', _StoreTrueAction) -+ self.register('action', 'store_false', _StoreFalseAction) -+ self.register('action', 'append', _AppendAction) -+ self.register('action', 'append_const', _AppendConstAction) -+ self.register('action', 'count', _CountAction) -+ self.register('action', 'help', _HelpAction) -+ self.register('action', 'version', _VersionAction) -+ self.register('action', 'parsers', _SubParsersAction) -+ -+ # raise an exception if the conflict handler is invalid -+ self._get_handler() -+ -+ # action storage -+ self._actions = [] -+ self._option_string_actions = {} -+ -+ # groups -+ self._action_groups = [] -+ self._mutually_exclusive_groups = [] -+ -+ # defaults storage -+ self._defaults = {} -+ -+ # determines whether an "option" looks like a negative number -+ self._negative_number_matcher = _re.compile(r'^-\d+|-\d*.\d+$') -+ -+ # whether or not there are any optionals that look like negative -+ # numbers -- uses a list so it can be shared and edited -+ self._has_negative_number_optionals = [] -+ -+ # ==================== -+ # Registration methods -+ # ==================== -+ def register(self, registry_name, value, object): -+ registry = self._registries.setdefault(registry_name, {}) -+ registry[value] = object -+ -+ def _registry_get(self, registry_name, value, default=None): -+ return self._registries[registry_name].get(value, default) -+ -+ # ================================== -+ # Namespace default settings methods -+ # ================================== -+ def set_defaults(self, **kwargs): -+ self._defaults.update(kwargs) -+ -+ # if these defaults match any existing arguments, replace -+ # the previous default on the object with the new one -+ for action in self._actions: -+ if action.dest in kwargs: -+ action.default = kwargs[action.dest] -+ -+ # ======================= -+ # Adding argument actions -+ # ======================= -+ def add_argument(self, *args, **kwargs): -+ """ -+ add_argument(dest, ..., name=value, ...) -+ add_argument(option_string, option_string, ..., name=value, ...) -+ """ -+ -+ # if no positional args are supplied or only one is supplied and -+ # it doesn't look like an option string, parse a positional -+ # argument -+ chars = self.prefix_chars -+ if not args or len(args) == 1 and args[0][0] not in chars: -+ kwargs = self._get_positional_kwargs(*args, **kwargs) -+ -+ # otherwise, we're adding an optional argument -+ else: -+ kwargs = self._get_optional_kwargs(*args, **kwargs) -+ -+ # if no default was supplied, use the parser-level default -+ if 'default' not in kwargs: -+ dest = kwargs['dest'] -+ if dest in self._defaults: -+ kwargs['default'] = self._defaults[dest] -+ elif self.argument_default is not None: -+ kwargs['default'] = self.argument_default -+ -+ # create the action object, and add it to the parser -+ action_class = self._pop_action_class(kwargs) -+ action = action_class(**kwargs) -+ return self._add_action(action) -+ -+ def add_argument_group(self, *args, **kwargs): -+ group = _ArgumentGroup(self, *args, **kwargs) -+ self._action_groups.append(group) -+ return group -+ -+ def add_mutually_exclusive_group(self, **kwargs): -+ group = _MutuallyExclusiveGroup(self, **kwargs) -+ self._mutually_exclusive_groups.append(group) -+ return group -+ -+ def _add_action(self, action): -+ # resolve any conflicts -+ self._check_conflict(action) -+ -+ # add to actions list -+ self._actions.append(action) -+ action.container = self -+ -+ # index the action by any option strings it has -+ for option_string in action.option_strings: -+ self._option_string_actions[option_string] = action -+ -+ # set the flag if any option strings look like negative numbers -+ for option_string in action.option_strings: -+ if self._negative_number_matcher.match(option_string): -+ if not self._has_negative_number_optionals: -+ self._has_negative_number_optionals.append(True) -+ -+ # return the created action -+ return action -+ -+ def _remove_action(self, action): -+ self._actions.remove(action) -+ -+ def _add_container_actions(self, container): -+ # collect groups by titles -+ title_group_map = {} -+ for group in self._action_groups: -+ if group.title in title_group_map: -+ msg = _('cannot merge actions - two groups are named %r') -+ raise ValueError(msg % (group.title)) -+ title_group_map[group.title] = group -+ -+ # map each action to its group -+ group_map = {} -+ for group in container._action_groups: -+ -+ # if a group with the title exists, use that, otherwise -+ # create a new group matching the container's group -+ if group.title not in title_group_map: -+ title_group_map[group.title] = self.add_argument_group( -+ title=group.title, -+ description=group.description, -+ conflict_handler=group.conflict_handler) -+ -+ # map the actions to their new group -+ for action in group._group_actions: -+ group_map[action] = title_group_map[group.title] -+ -+ # add all actions to this container or their group -+ for action in container._actions: -+ group_map.get(action, self)._add_action(action) -+ -+ def _get_positional_kwargs(self, dest, **kwargs): -+ # make sure required is not specified -+ if 'required' in kwargs: -+ msg = _("'required' is an invalid argument for positionals") -+ raise TypeError(msg) -+ -+ # mark positional arguments as required if at least one is -+ # always required -+ if kwargs.get('nargs') not in [OPTIONAL, ZERO_OR_MORE]: -+ kwargs['required'] = True -+ if kwargs.get('nargs') == ZERO_OR_MORE and 'default' not in kwargs: -+ kwargs['required'] = True -+ -+ # return the keyword arguments with no option strings -+ return dict(kwargs, dest=dest, option_strings=[]) -+ -+ def _get_optional_kwargs(self, *args, **kwargs): -+ # determine short and long option strings -+ option_strings = [] -+ long_option_strings = [] -+ for option_string in args: -+ # error on one-or-fewer-character option strings -+ if len(option_string) < 2: -+ msg = _('invalid option string %r: ' -+ 'must be at least two characters long') -+ raise ValueError(msg % option_string) -+ -+ # error on strings that don't start with an appropriate prefix -+ if not option_string[0] in self.prefix_chars: -+ msg = _('invalid option string %r: ' -+ 'must start with a character %r') -+ tup = option_string, self.prefix_chars -+ raise ValueError(msg % tup) -+ -+ # error on strings that are all prefix characters -+ if not (_set(option_string) - _set(self.prefix_chars)): -+ msg = _('invalid option string %r: ' -+ 'must contain characters other than %r') -+ tup = option_string, self.prefix_chars -+ raise ValueError(msg % tup) -+ -+ # strings starting with two prefix characters are long options -+ option_strings.append(option_string) -+ if option_string[0] in self.prefix_chars: -+ if option_string[1] in self.prefix_chars: -+ long_option_strings.append(option_string) -+ -+ # infer destination, '--foo-bar' -> 'foo_bar' and '-x' -> 'x' -+ dest = kwargs.pop('dest', None) -+ if dest is None: -+ if long_option_strings: -+ dest_option_string = long_option_strings[0] -+ else: -+ dest_option_string = option_strings[0] -+ dest = dest_option_string.lstrip(self.prefix_chars) -+ dest = dest.replace('-', '_') -+ -+ # return the updated keyword arguments -+ return dict(kwargs, dest=dest, option_strings=option_strings) -+ -+ def _pop_action_class(self, kwargs, default=None): -+ action = kwargs.pop('action', default) -+ return self._registry_get('action', action, action) -+ -+ def _get_handler(self): -+ # determine function from conflict handler string -+ handler_func_name = '_handle_conflict_%s' % self.conflict_handler -+ try: -+ return getattr(self, handler_func_name) -+ except AttributeError: -+ msg = _('invalid conflict_resolution value: %r') -+ raise ValueError(msg % self.conflict_handler) -+ -+ def _check_conflict(self, action): -+ -+ # find all options that conflict with this option -+ confl_optionals = [] -+ for option_string in action.option_strings: -+ if option_string in self._option_string_actions: -+ confl_optional = self._option_string_actions[option_string] -+ confl_optionals.append((option_string, confl_optional)) -+ -+ # resolve any conflicts -+ if confl_optionals: -+ conflict_handler = self._get_handler() -+ conflict_handler(action, confl_optionals) -+ -+ def _handle_conflict_error(self, action, conflicting_actions): -+ message = _('conflicting option string(s): %s') -+ conflict_string = ', '.join([option_string -+ for option_string, action -+ in conflicting_actions]) -+ raise ArgumentError(action, message % conflict_string) -+ -+ def _handle_conflict_resolve(self, action, conflicting_actions): -+ -+ # remove all conflicting options -+ for option_string, action in conflicting_actions: -+ -+ # remove the conflicting option -+ action.option_strings.remove(option_string) -+ self._option_string_actions.pop(option_string, None) -+ -+ # if the option now has no option string, remove it from the -+ # container holding it -+ if not action.option_strings: -+ action.container._remove_action(action) -+ -+ -+class _ArgumentGroup(_ActionsContainer): -+ -+ def __init__(self, container, title=None, description=None, **kwargs): -+ # add any missing keyword arguments by checking the container -+ update = kwargs.setdefault -+ update('conflict_handler', container.conflict_handler) -+ update('prefix_chars', container.prefix_chars) -+ update('argument_default', container.argument_default) -+ super_init = super(_ArgumentGroup, self).__init__ -+ super_init(description=description, **kwargs) -+ -+ # group attributes -+ self.title = title -+ self._group_actions = [] -+ -+ # share most attributes with the container -+ self._registries = container._registries -+ self._actions = container._actions -+ self._option_string_actions = container._option_string_actions -+ self._defaults = container._defaults -+ self._has_negative_number_optionals = \ -+ container._has_negative_number_optionals -+ -+ def _add_action(self, action): -+ action = super(_ArgumentGroup, self)._add_action(action) -+ self._group_actions.append(action) -+ return action -+ -+ def _remove_action(self, action): -+ super(_ArgumentGroup, self)._remove_action(action) -+ self._group_actions.remove(action) -+ -+ -+class _MutuallyExclusiveGroup(_ArgumentGroup): -+ -+ def __init__(self, container, required=False): -+ super(_MutuallyExclusiveGroup, self).__init__(container) -+ self.required = required -+ self._container = container -+ -+ def _add_action(self, action): -+ if action.required: -+ msg = _('mutually exclusive arguments must be optional') -+ raise ValueError(msg) -+ action = self._container._add_action(action) -+ self._group_actions.append(action) -+ return action -+ -+ def _remove_action(self, action): -+ self._container._remove_action(action) -+ self._group_actions.remove(action) -+ -+ -+class ArgumentParser(_AttributeHolder, _ActionsContainer): -+ """Object for parsing command line strings into Python objects. -+ -+ Keyword Arguments: -+ - prog -- The name of the program (default: sys.argv[0]) -+ - usage -- A usage message (default: auto-generated from arguments) -+ - description -- A description of what the program does -+ - epilog -- Text following the argument descriptions -+ - version -- Add a -v/--version option with the given version string -+ - parents -- Parsers whose arguments should be copied into this one -+ - formatter_class -- HelpFormatter class for printing help messages -+ - prefix_chars -- Characters that prefix optional arguments -+ - fromfile_prefix_chars -- Characters that prefix files containing -+ additional arguments -+ - argument_default -- The default value for all arguments -+ - conflict_handler -- String indicating how to handle conflicts -+ - add_help -- Add a -h/-help option -+ """ -+ -+ def __init__(self, -+ prog=None, -+ usage=None, -+ description=None, -+ epilog=None, -+ version=None, -+ parents=[], -+ formatter_class=HelpFormatter, -+ prefix_chars='-', -+ fromfile_prefix_chars=None, -+ argument_default=None, -+ conflict_handler='error', -+ add_help=True): -+ -+ superinit = super(ArgumentParser, self).__init__ -+ superinit(description=description, -+ prefix_chars=prefix_chars, -+ argument_default=argument_default, -+ conflict_handler=conflict_handler) -+ -+ # default setting for prog -+ if prog is None: -+ prog = _os.path.basename(_sys.argv[0]) -+ -+ self.prog = prog -+ self.usage = usage -+ self.epilog = epilog -+ self.version = version -+ self.formatter_class = formatter_class -+ self.fromfile_prefix_chars = fromfile_prefix_chars -+ self.add_help = add_help -+ -+ add_group = self.add_argument_group -+ self._positionals = add_group(_('positional arguments')) -+ self._optionals = add_group(_('optional arguments')) -+ self._subparsers = None -+ -+ # register types -+ def identity(string): -+ return string -+ self.register('type', None, identity) -+ -+ # add help and version arguments if necessary -+ # (using explicit default to override global argument_default) -+ if self.add_help: -+ self.add_argument( -+ '-h', '--help', action='help', default=SUPPRESS, -+ help=_('show this help message and exit')) -+ if self.version: -+ self.add_argument( -+ '-v', '--version', action='version', default=SUPPRESS, -+ help=_("show program's version number and exit")) -+ -+ # add parent arguments and defaults -+ for parent in parents: -+ self._add_container_actions(parent) -+ try: -+ defaults = parent._defaults -+ except AttributeError: -+ pass -+ else: -+ self._defaults.update(defaults) -+ -+ # ======================= -+ # Pretty __repr__ methods -+ # ======================= -+ def _get_kwargs(self): -+ names = [ -+ 'prog', -+ 'usage', -+ 'description', -+ 'version', -+ 'formatter_class', -+ 'conflict_handler', -+ 'add_help', -+ ] -+ return [(name, getattr(self, name)) for name in names] -+ -+ # ================================== -+ # Optional/Positional adding methods -+ # ================================== -+ def add_subparsers(self, **kwargs): -+ if self._subparsers is not None: -+ self.error(_('cannot have multiple subparser arguments')) -+ -+ # add the parser class to the arguments if it's not present -+ kwargs.setdefault('parser_class', type(self)) -+ -+ if 'title' in kwargs or 'description' in kwargs: -+ title = _(kwargs.pop('title', 'subcommands')) -+ description = _(kwargs.pop('description', None)) -+ self._subparsers = self.add_argument_group(title, description) -+ else: -+ self._subparsers = self._positionals -+ -+ # prog defaults to the usage message of this parser, skipping -+ # optional arguments and with no "usage:" prefix -+ if kwargs.get('prog') is None: -+ formatter = self._get_formatter() -+ positionals = self._get_positional_actions() -+ groups = self._mutually_exclusive_groups -+ formatter.add_usage(self.usage, positionals, groups, '') -+ kwargs['prog'] = formatter.format_help().strip() -+ -+ # create the parsers action and add it to the positionals list -+ parsers_class = self._pop_action_class(kwargs, 'parsers') -+ action = parsers_class(option_strings=[], **kwargs) -+ self._subparsers._add_action(action) -+ -+ # return the created parsers action -+ return action -+ -+ def _add_action(self, action): -+ if action.option_strings: -+ self._optionals._add_action(action) -+ else: -+ self._positionals._add_action(action) -+ return action -+ -+ def _get_optional_actions(self): -+ return [action -+ for action in self._actions -+ if action.option_strings] -+ -+ def _get_positional_actions(self): -+ return [action -+ for action in self._actions -+ if not action.option_strings] -+ -+ # ===================================== -+ # Command line argument parsing methods -+ # ===================================== -+ def parse_args(self, args=None, namespace=None): -+ args, argv = self.parse_known_args(args, namespace) -+ if argv: -+ msg = _('unrecognized arguments: %s') -+ self.error(msg % ' '.join(argv)) -+ return args -+ -+ def parse_known_args(self, args=None, namespace=None): -+ # args default to the system args -+ if args is None: -+ args = _sys.argv[1:] -+ -+ # default Namespace built from parser defaults -+ if namespace is None: -+ namespace = Namespace() -+ -+ # add any action defaults that aren't present -+ for action in self._actions: -+ if action.dest is not SUPPRESS: -+ if not hasattr(namespace, action.dest): -+ if action.default is not SUPPRESS: -+ default = action.default -+ if isinstance(action.default, _basestring): -+ default = self._get_value(action, default) -+ setattr(namespace, action.dest, default) -+ -+ # add any parser defaults that aren't present -+ for dest in self._defaults: -+ if not hasattr(namespace, dest): -+ setattr(namespace, dest, self._defaults[dest]) -+ -+ # parse the arguments and exit if there are any errors -+ try: -+ return self._parse_known_args(args, namespace) -+ except ArgumentError: -+ err = _sys.exc_info()[1] -+ self.error(str(err)) -+ -+ def _parse_known_args(self, arg_strings, namespace): -+ # replace arg strings that are file references -+ if self.fromfile_prefix_chars is not None: -+ arg_strings = self._read_args_from_files(arg_strings) -+ -+ # map all mutually exclusive arguments to the other arguments -+ # they can't occur with -+ action_conflicts = {} -+ for mutex_group in self._mutually_exclusive_groups: -+ group_actions = mutex_group._group_actions -+ for i, mutex_action in enumerate(mutex_group._group_actions): -+ conflicts = action_conflicts.setdefault(mutex_action, []) -+ conflicts.extend(group_actions[:i]) -+ conflicts.extend(group_actions[i + 1:]) -+ -+ # find all option indices, and determine the arg_string_pattern -+ # which has an 'O' if there is an option at an index, -+ # an 'A' if there is an argument, or a '-' if there is a '--' -+ option_string_indices = {} -+ arg_string_pattern_parts = [] -+ arg_strings_iter = iter(arg_strings) -+ for i, arg_string in enumerate(arg_strings_iter): -+ -+ # all args after -- are non-options -+ if arg_string == '--': -+ arg_string_pattern_parts.append('-') -+ for arg_string in arg_strings_iter: -+ arg_string_pattern_parts.append('A') -+ -+ # otherwise, add the arg to the arg strings -+ # and note the index if it was an option -+ else: -+ option_tuple = self._parse_optional(arg_string) -+ if option_tuple is None: -+ pattern = 'A' -+ else: -+ option_string_indices[i] = option_tuple -+ pattern = 'O' -+ arg_string_pattern_parts.append(pattern) -+ -+ # join the pieces together to form the pattern -+ arg_strings_pattern = ''.join(arg_string_pattern_parts) -+ -+ # converts arg strings to the appropriate and then takes the action -+ seen_actions = _set() -+ seen_non_default_actions = _set() -+ -+ def take_action(action, argument_strings, option_string=None): -+ seen_actions.add(action) -+ argument_values = self._get_values(action, argument_strings) -+ -+ # error if this argument is not allowed with other previously -+ # seen arguments, assuming that actions that use the default -+ # value don't really count as "present" -+ if argument_values is not action.default: -+ seen_non_default_actions.add(action) -+ for conflict_action in action_conflicts.get(action, []): -+ if conflict_action in seen_non_default_actions: -+ msg = _('not allowed with argument %s') -+ action_name = _get_action_name(conflict_action) -+ raise ArgumentError(action, msg % action_name) -+ -+ # take the action if we didn't receive a SUPPRESS value -+ # (e.g. from a default) -+ if argument_values is not SUPPRESS: -+ action(self, namespace, argument_values, option_string) -+ -+ # function to convert arg_strings into an optional action -+ def consume_optional(start_index): -+ -+ # get the optional identified at this index -+ option_tuple = option_string_indices[start_index] -+ action, option_string, explicit_arg = option_tuple -+ -+ # identify additional optionals in the same arg string -+ # (e.g. -xyz is the same as -x -y -z if no args are required) -+ match_argument = self._match_argument -+ action_tuples = [] -+ while True: -+ -+ # if we found no optional action, skip it -+ if action is None: -+ extras.append(arg_strings[start_index]) -+ return start_index + 1 -+ -+ # if there is an explicit argument, try to match the -+ # optional's string arguments to only this -+ if explicit_arg is not None: -+ arg_count = match_argument(action, 'A') -+ -+ # if the action is a single-dash option and takes no -+ # arguments, try to parse more single-dash options out -+ # of the tail of the option string -+ chars = self.prefix_chars -+ if arg_count == 0 and option_string[1] not in chars: -+ action_tuples.append((action, [], option_string)) -+ for char in self.prefix_chars: -+ option_string = char + explicit_arg[0] -+ explicit_arg = explicit_arg[1:] or None -+ optionals_map = self._option_string_actions -+ if option_string in optionals_map: -+ action = optionals_map[option_string] -+ break -+ else: -+ msg = _('ignored explicit argument %r') -+ raise ArgumentError(action, msg % explicit_arg) -+ -+ # if the action expect exactly one argument, we've -+ # successfully matched the option; exit the loop -+ elif arg_count == 1: -+ stop = start_index + 1 -+ args = [explicit_arg] -+ action_tuples.append((action, args, option_string)) -+ break -+ -+ # error if a double-dash option did not use the -+ # explicit argument -+ else: -+ msg = _('ignored explicit argument %r') -+ raise ArgumentError(action, msg % explicit_arg) -+ -+ # if there is no explicit argument, try to match the -+ # optional's string arguments with the following strings -+ # if successful, exit the loop -+ else: -+ start = start_index + 1 -+ selected_patterns = arg_strings_pattern[start:] -+ arg_count = match_argument(action, selected_patterns) -+ stop = start + arg_count -+ args = arg_strings[start:stop] -+ action_tuples.append((action, args, option_string)) -+ break -+ -+ # add the Optional to the list and return the index at which -+ # the Optional's string args stopped -+ assert action_tuples -+ for action, args, option_string in action_tuples: -+ take_action(action, args, option_string) -+ return stop -+ -+ # the list of Positionals left to be parsed; this is modified -+ # by consume_positionals() -+ positionals = self._get_positional_actions() -+ -+ # function to convert arg_strings into positional actions -+ def consume_positionals(start_index): -+ # match as many Positionals as possible -+ match_partial = self._match_arguments_partial -+ selected_pattern = arg_strings_pattern[start_index:] -+ arg_counts = match_partial(positionals, selected_pattern) -+ -+ # slice off the appropriate arg strings for each Positional -+ # and add the Positional and its args to the list -+ for action, arg_count in zip(positionals, arg_counts): -+ args = arg_strings[start_index: start_index + arg_count] -+ start_index += arg_count -+ take_action(action, args) -+ -+ # slice off the Positionals that we just parsed and return the -+ # index at which the Positionals' string args stopped -+ positionals[:] = positionals[len(arg_counts):] -+ return start_index -+ -+ # consume Positionals and Optionals alternately, until we have -+ # passed the last option string -+ extras = [] -+ start_index = 0 -+ if option_string_indices: -+ max_option_string_index = max(option_string_indices) -+ else: -+ max_option_string_index = -1 -+ while start_index <= max_option_string_index: -+ -+ # consume any Positionals preceding the next option -+ next_option_string_index = min([ -+ index -+ for index in option_string_indices -+ if index >= start_index]) -+ if start_index != next_option_string_index: -+ positionals_end_index = consume_positionals(start_index) -+ -+ # only try to parse the next optional if we didn't consume -+ # the option string during the positionals parsing -+ if positionals_end_index > start_index: -+ start_index = positionals_end_index -+ continue -+ else: -+ start_index = positionals_end_index -+ -+ # if we consumed all the positionals we could and we're not -+ # at the index of an option string, there were extra arguments -+ if start_index not in option_string_indices: -+ strings = arg_strings[start_index:next_option_string_index] -+ extras.extend(strings) -+ start_index = next_option_string_index -+ -+ # consume the next optional and any arguments for it -+ start_index = consume_optional(start_index) -+ -+ # consume any positionals following the last Optional -+ stop_index = consume_positionals(start_index) -+ -+ # if we didn't consume all the argument strings, there were extras -+ extras.extend(arg_strings[stop_index:]) -+ -+ # if we didn't use all the Positional objects, there were too few -+ # arg strings supplied. -+ if positionals: -+ self.error(_('too few arguments')) -+ -+ # make sure all required actions were present -+ for action in self._actions: -+ if action.required: -+ if action not in seen_actions: -+ name = _get_action_name(action) -+ self.error(_('argument %s is required') % name) -+ -+ # make sure all required groups had one option present -+ for group in self._mutually_exclusive_groups: -+ if group.required: -+ for action in group._group_actions: -+ if action in seen_non_default_actions: -+ break -+ -+ # if no actions were used, report the error -+ else: -+ names = [_get_action_name(action) -+ for action in group._group_actions -+ if action.help is not SUPPRESS] -+ msg = _('one of the arguments %s is required') -+ self.error(msg % ' '.join(names)) -+ -+ # return the updated namespace and the extra arguments -+ return namespace, extras -+ -+ def _read_args_from_files(self, arg_strings): -+ # expand arguments referencing files -+ new_arg_strings = [] -+ for arg_string in arg_strings: -+ -+ # for regular arguments, just add them back into the list -+ if arg_string[0] not in self.fromfile_prefix_chars: -+ new_arg_strings.append(arg_string) -+ -+ # replace arguments referencing files with the file content -+ else: -+ try: -+ args_file = open(arg_string[1:]) -+ try: -+ arg_strings = args_file.read().splitlines() -+ arg_strings = self._read_args_from_files(arg_strings) -+ new_arg_strings.extend(arg_strings) -+ finally: -+ args_file.close() -+ except IOError: -+ err = _sys.exc_info()[1] -+ self.error(str(err)) -+ -+ # return the modified argument list -+ return new_arg_strings -+ -+ def _match_argument(self, action, arg_strings_pattern): -+ # match the pattern for this action to the arg strings -+ nargs_pattern = self._get_nargs_pattern(action) -+ match = _re.match(nargs_pattern, arg_strings_pattern) -+ -+ # raise an exception if we weren't able to find a match -+ if match is None: -+ nargs_errors = { -+ None: _('expected one argument'), -+ OPTIONAL: _('expected at most one argument'), -+ ONE_OR_MORE: _('expected at least one argument'), -+ } -+ default = _('expected %s argument(s)') % action.nargs -+ msg = nargs_errors.get(action.nargs, default) -+ raise ArgumentError(action, msg) -+ -+ # return the number of arguments matched -+ return len(match.group(1)) -+ -+ def _match_arguments_partial(self, actions, arg_strings_pattern): -+ # progressively shorten the actions list by slicing off the -+ # final actions until we find a match -+ result = [] -+ for i in range(len(actions), 0, -1): -+ actions_slice = actions[:i] -+ pattern = ''.join([self._get_nargs_pattern(action) -+ for action in actions_slice]) -+ match = _re.match(pattern, arg_strings_pattern) -+ if match is not None: -+ result.extend([len(string) for string in match.groups()]) -+ break -+ -+ # return the list of arg string counts -+ return result -+ -+ def _parse_optional(self, arg_string): -+ # if it's an empty string, it was meant to be a positional -+ if not arg_string: -+ return None -+ -+ # if it doesn't start with a prefix, it was meant to be positional -+ if not arg_string[0] in self.prefix_chars: -+ return None -+ -+ # if it's just dashes, it was meant to be positional -+ if not arg_string.strip('-'): -+ return None -+ -+ # if the option string is present in the parser, return the action -+ if arg_string in self._option_string_actions: -+ action = self._option_string_actions[arg_string] -+ return action, arg_string, None -+ -+ # search through all possible prefixes of the option string -+ # and all actions in the parser for possible interpretations -+ option_tuples = self._get_option_tuples(arg_string) -+ -+ # if multiple actions match, the option string was ambiguous -+ if len(option_tuples) > 1: -+ options = ', '.join([option_string -+ for action, option_string, explicit_arg in option_tuples]) -+ tup = arg_string, options -+ self.error(_('ambiguous option: %s could match %s') % tup) -+ -+ # if exactly one action matched, this segmentation is good, -+ # so return the parsed action -+ elif len(option_tuples) == 1: -+ option_tuple, = option_tuples -+ return option_tuple -+ -+ # if it was not found as an option, but it looks like a negative -+ # number, it was meant to be positional -+ # unless there are negative-number-like options -+ if self._negative_number_matcher.match(arg_string): -+ if not self._has_negative_number_optionals: -+ return None -+ -+ # if it contains a space, it was meant to be a positional -+ if ' ' in arg_string: -+ return None -+ -+ # it was meant to be an optional but there is no such option -+ # in this parser (though it might be a valid option in a subparser) -+ return None, arg_string, None -+ -+ def _get_option_tuples(self, option_string): -+ result = [] -+ -+ # option strings starting with two prefix characters are only -+ # split at the '=' -+ chars = self.prefix_chars -+ if option_string[0] in chars and option_string[1] in chars: -+ if '=' in option_string: -+ option_prefix, explicit_arg = option_string.split('=', 1) -+ else: -+ option_prefix = option_string -+ explicit_arg = None -+ for option_string in self._option_string_actions: -+ if option_string.startswith(option_prefix): -+ action = self._option_string_actions[option_string] -+ tup = action, option_string, explicit_arg -+ result.append(tup) -+ -+ # single character options can be concatenated with their arguments -+ # but multiple character options always have to have their argument -+ # separate -+ elif option_string[0] in chars and option_string[1] not in chars: -+ option_prefix = option_string -+ explicit_arg = None -+ short_option_prefix = option_string[:2] -+ short_explicit_arg = option_string[2:] -+ -+ for option_string in self._option_string_actions: -+ if option_string == short_option_prefix: -+ action = self._option_string_actions[option_string] -+ tup = action, option_string, short_explicit_arg -+ result.append(tup) -+ elif option_string.startswith(option_prefix): -+ action = self._option_string_actions[option_string] -+ tup = action, option_string, explicit_arg -+ result.append(tup) -+ -+ # shouldn't ever get here -+ else: -+ self.error(_('unexpected option string: %s') % option_string) -+ -+ # return the collected option tuples -+ return result -+ -+ def _get_nargs_pattern(self, action): -+ # in all examples below, we have to allow for '--' args -+ # which are represented as '-' in the pattern -+ nargs = action.nargs -+ -+ # the default (None) is assumed to be a single argument -+ if nargs is None: -+ nargs_pattern = '(-*A-*)' -+ -+ # allow zero or one arguments -+ elif nargs == OPTIONAL: -+ nargs_pattern = '(-*A?-*)' -+ -+ # allow zero or more arguments -+ elif nargs == ZERO_OR_MORE: -+ nargs_pattern = '(-*[A-]*)' -+ -+ # allow one or more arguments -+ elif nargs == ONE_OR_MORE: -+ nargs_pattern = '(-*A[A-]*)' -+ -+ # allow one argument followed by any number of options or arguments -+ elif nargs is PARSER: -+ nargs_pattern = '(-*A[-AO]*)' -+ -+ # all others should be integers -+ else: -+ nargs_pattern = '(-*%s-*)' % '-*'.join('A' * nargs) -+ -+ # if this is an optional action, -- is not allowed -+ if action.option_strings: -+ nargs_pattern = nargs_pattern.replace('-*', '') -+ nargs_pattern = nargs_pattern.replace('-', '') -+ -+ # return the pattern -+ return nargs_pattern -+ -+ # ======================== -+ # Value conversion methods -+ # ======================== -+ def _get_values(self, action, arg_strings): -+ # for everything but PARSER args, strip out '--' -+ if action.nargs is not PARSER: -+ arg_strings = [s for s in arg_strings if s != '--'] -+ -+ # optional argument produces a default when not present -+ if not arg_strings and action.nargs == OPTIONAL: -+ if action.option_strings: -+ value = action.const -+ else: -+ value = action.default -+ if isinstance(value, _basestring): -+ value = self._get_value(action, value) -+ self._check_value(action, value) -+ -+ # when nargs='*' on a positional, if there were no command-line -+ # args, use the default if it is anything other than None -+ elif (not arg_strings and action.nargs == ZERO_OR_MORE and -+ not action.option_strings): -+ if action.default is not None: -+ value = action.default -+ else: -+ value = arg_strings -+ self._check_value(action, value) -+ -+ # single argument or optional argument produces a single value -+ elif len(arg_strings) == 1 and action.nargs in [None, OPTIONAL]: -+ arg_string, = arg_strings -+ value = self._get_value(action, arg_string) -+ self._check_value(action, value) -+ -+ # PARSER arguments convert all values, but check only the first -+ elif action.nargs is PARSER: -+ value = [self._get_value(action, v) for v in arg_strings] -+ self._check_value(action, value[0]) -+ -+ # all other types of nargs produce a list -+ else: -+ value = [self._get_value(action, v) for v in arg_strings] -+ for v in value: -+ self._check_value(action, v) -+ -+ # return the converted value -+ return value -+ -+ def _get_value(self, action, arg_string): -+ type_func = self._registry_get('type', action.type, action.type) -+ if not hasattr(type_func, '__call__'): -+ msg = _('%r is not callable') -+ raise ArgumentError(action, msg % type_func) -+ -+ # convert the value to the appropriate type -+ try: -+ result = type_func(arg_string) -+ -+ # TypeErrors or ValueErrors indicate errors -+ except (TypeError, ValueError): -+ name = getattr(action.type, '__name__', repr(action.type)) -+ msg = _('invalid %s value: %r') -+ raise ArgumentError(action, msg % (name, arg_string)) -+ -+ # return the converted value -+ return result -+ -+ def _check_value(self, action, value): -+ # converted value must be one of the choices (if specified) -+ if action.choices is not None and value not in action.choices: -+ tup = value, ', '.join(map(repr, action.choices)) -+ msg = _('invalid choice: %r (choose from %s)') % tup -+ raise ArgumentError(action, msg) -+ -+ # ======================= -+ # Help-formatting methods -+ # ======================= -+ def format_usage(self): -+ formatter = self._get_formatter() -+ formatter.add_usage(self.usage, self._actions, -+ self._mutually_exclusive_groups) -+ return formatter.format_help() -+ -+ def format_help(self): -+ formatter = self._get_formatter() -+ -+ # usage -+ formatter.add_usage(self.usage, self._actions, -+ self._mutually_exclusive_groups) -+ -+ # description -+ formatter.add_text(self.description) -+ -+ # positionals, optionals and user-defined groups -+ for action_group in self._action_groups: -+ formatter.start_section(action_group.title) -+ formatter.add_text(action_group.description) -+ formatter.add_arguments(action_group._group_actions) -+ formatter.end_section() -+ -+ # epilog -+ formatter.add_text(self.epilog) -+ -+ # determine help from format above -+ return formatter.format_help() -+ -+ def format_version(self): -+ formatter = self._get_formatter() -+ formatter.add_text(self.version) -+ return formatter.format_help() -+ -+ def _get_formatter(self): -+ return self.formatter_class(prog=self.prog) -+ -+ # ===================== -+ # Help-printing methods -+ # ===================== -+ def print_usage(self, file=None): -+ self._print_message(self.format_usage(), file) -+ -+ def print_help(self, file=None): -+ self._print_message(self.format_help(), file) -+ -+ def print_version(self, file=None): -+ self._print_message(self.format_version(), file) -+ -+ def _print_message(self, message, file=None): -+ if message: -+ if file is None: -+ file = _sys.stderr -+ file.write(message) -+ -+ # =============== -+ # Exiting methods -+ # =============== -+ def exit(self, status=0, message=None): -+ if message: -+ _sys.stderr.write(message) -+ _sys.exit(status) -+ -+ def error(self, message): -+ """error(message: string) -+ -+ Prints a usage message incorporating the message to stderr and -+ exits. -+ -+ 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.prog, message)) Index: ipython-0.10/IPython/external/argparse/__init__.py =================================================================== --- /dev/null @@ -2235,4733 +14,6 @@ Index: ipython-0.10/IPython/external/argparse/__init__.py + from argparse import * +except ImportError: + from _argparse import * -Index: ipython-0.10/IPython/external/argparse.py -=================================================================== ---- ipython-0.10.orig/IPython/external/argparse.py -+++ /dev/null -@@ -1,2216 +0,0 @@ --# -*- coding: utf-8 -*- -- --# Copyright © 2006-2009 Steven J. Bethard . --# --# Licensed under the Apache License, Version 2.0 (the "License"); you may not --# use this file except in compliance with the License. You may obtain a copy --# of the License at --# --# http://www.apache.org/licenses/LICENSE-2.0 --# --# Unless required by applicable law or agreed to in writing, software --# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT --# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the --# License for the specific language governing permissions and limitations --# under the License. -- --"""Command-line parsing library -- --This module is an optparse-inspired command-line parsing library that: -- -- - handles both optional and positional arguments -- - produces highly informative usage messages -- - supports parsers that dispatch to sub-parsers -- --The following is a simple usage example that sums integers from the --command-line and writes the result to a file:: -- -- parser = argparse.ArgumentParser( -- description='sum the integers at the command line') -- parser.add_argument( -- 'integers', metavar='int', nargs='+', type=int, -- help='an integer to be summed') -- parser.add_argument( -- '--log', default=sys.stdout, type=argparse.FileType('w'), -- help='the file where the sum should be written') -- args = parser.parse_args() -- args.log.write('%s' % sum(args.integers)) -- args.log.close() -- --The module contains the following public classes: -- -- - ArgumentParser -- The main entry point for command-line parsing. As the -- example above shows, the add_argument() method is used to populate -- the parser with actions for optional and positional arguments. Then -- the parse_args() method is invoked to convert the args at the -- command-line into an object with attributes. -- -- - ArgumentError -- The exception raised by ArgumentParser objects when -- there are errors with the parser's actions. Errors raised while -- parsing the command-line are caught by ArgumentParser and emitted -- as command-line messages. -- -- - FileType -- A factory for defining types of files to be created. As the -- example above shows, instances of FileType are typically passed as -- the type= argument of add_argument() calls. -- -- - Action -- The base class for parser actions. Typically actions are -- selected by passing strings like 'store_true' or 'append_const' to -- the action= argument of add_argument(). However, for greater -- customization of ArgumentParser actions, subclasses of Action may -- be defined and passed as the action= argument. -- -- - HelpFormatter, RawDescriptionHelpFormatter, RawTextHelpFormatter, -- ArgumentDefaultsHelpFormatter -- Formatter classes which -- may be passed as the formatter_class= argument to the -- ArgumentParser constructor. HelpFormatter is the default, -- RawDescriptionHelpFormatter and RawTextHelpFormatter tell the parser -- not to change the formatting for help text, and -- ArgumentDefaultsHelpFormatter adds information about argument defaults -- to the help. -- --All other classes in this module are considered implementation details. --(Also note that HelpFormatter and RawDescriptionHelpFormatter are only --considered public as object names -- the API of the formatter objects is --still considered an implementation detail.) --""" -- --__version__ = '1.0' --__all__ = [ -- 'ArgumentParser', -- 'ArgumentError', -- 'Namespace', -- 'Action', -- 'FileType', -- 'HelpFormatter', -- 'RawDescriptionHelpFormatter', -- 'RawTextHelpFormatter' -- 'ArgumentDefaultsHelpFormatter', --] -- -- --import copy as _copy --import os as _os --import re as _re --import sys as _sys --import textwrap as _textwrap -- --from gettext import gettext as _ -- --try: -- _set = set --except NameError: -- from sets import Set as _set -- --try: -- _basestring = basestring --except NameError: -- _basestring = str -- --try: -- _sorted = sorted --except NameError: -- -- def _sorted(iterable, reverse=False): -- result = list(iterable) -- result.sort() -- if reverse: -- result.reverse() -- return result -- -- --SUPPRESS = '==SUPPRESS==' -- --OPTIONAL = '?' --ZERO_OR_MORE = '*' --ONE_OR_MORE = '+' --PARSER = '==PARSER==' -- --# ============================= --# Utility functions and classes --# ============================= -- --class _AttributeHolder(object): -- """Abstract base class that provides __repr__. -- -- The __repr__ method returns a string in the format:: -- ClassName(attr=name, attr=name, ...) -- The attributes are determined either by a class-level attribute, -- '_kwarg_names', or by inspecting the instance __dict__. -- """ -- -- def __repr__(self): -- type_name = type(self).__name__ -- arg_strings = [] -- for arg in self._get_args(): -- arg_strings.append(repr(arg)) -- for name, value in self._get_kwargs(): -- arg_strings.append('%s=%r' % (name, value)) -- return '%s(%s)' % (type_name, ', '.join(arg_strings)) -- -- def _get_kwargs(self): -- return _sorted(self.__dict__.items()) -- -- def _get_args(self): -- return [] -- -- --def _ensure_value(namespace, name, value): -- if getattr(namespace, name, None) is None: -- setattr(namespace, name, value) -- return getattr(namespace, name) -- -- --# =============== --# Formatting Help --# =============== -- --class HelpFormatter(object): -- """Formatter for generating usage messages and argument help strings. -- -- Only the name of this class is considered a public API. All the methods -- provided by the class are considered an implementation detail. -- """ -- -- def __init__(self, -- prog, -- indent_increment=2, -- max_help_position=24, -- width=None): -- -- # default setting for width -- if width is None: -- try: -- width = int(_os.environ['COLUMNS']) -- except (KeyError, ValueError): -- width = 80 -- width -= 2 -- -- self._prog = prog -- self._indent_increment = indent_increment -- self._max_help_position = max_help_position -- self._width = width -- -- self._current_indent = 0 -- self._level = 0 -- self._action_max_length = 0 -- -- self._root_section = self._Section(self, None) -- self._current_section = self._root_section -- -- self._whitespace_matcher = _re.compile(r'\s+') -- self._long_break_matcher = _re.compile(r'\n\n\n+') -- -- # =============================== -- # Section and indentation methods -- # =============================== -- 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 -- -- class _Section(object): -- -- def __init__(self, formatter, parent, heading=None): -- self.formatter = formatter -- self.parent = parent -- self.heading = heading -- self.items = [] -- -- def format_help(self): -- # format the indented section -- if self.parent is not None: -- self.formatter._indent() -- join = self.formatter._join_parts -- for func, args in self.items: -- func(*args) -- item_help = join([func(*args) for func, args in self.items]) -- if self.parent is not None: -- self.formatter._dedent() -- -- # return nothing if the section was empty -- if not item_help: -- return '' -- -- # add the heading if the section was non-empty -- if self.heading is not SUPPRESS and self.heading is not None: -- current_indent = self.formatter._current_indent -- heading = '%*s%s:\n' % (current_indent, '', self.heading) -- else: -- heading = '' -- -- # join the section-initial newline, the heading and the help -- return join(['\n', heading, item_help, '\n']) -- -- def _add_item(self, func, args): -- self._current_section.items.append((func, args)) -- -- # ======================== -- # Message building methods -- # ======================== -- def start_section(self, heading): -- self._indent() -- section = self._Section(self, self._current_section, heading) -- self._add_item(section.format_help, []) -- self._current_section = section -- -- def end_section(self): -- self._current_section = self._current_section.parent -- self._dedent() -- -- def add_text(self, text): -- if text is not SUPPRESS and text is not None: -- self._add_item(self._format_text, [text]) -- -- def add_usage(self, usage, actions, groups, prefix=None): -- if usage is not SUPPRESS: -- args = usage, actions, groups, prefix -- self._add_item(self._format_usage, args) -- -- def add_argument(self, action): -- if action.help is not SUPPRESS: -- -- # find all invocations -- get_invocation = self._format_action_invocation -- invocations = [get_invocation(action)] -- for subaction in self._iter_indented_subactions(action): -- invocations.append(get_invocation(subaction)) -- -- # update the maximum item length -- invocation_length = max([len(s) for s in invocations]) -- action_length = invocation_length + self._current_indent -- self._action_max_length = max(self._action_max_length, -- action_length) -- -- # add the item to the list -- self._add_item(self._format_action, [action]) -- -- def add_arguments(self, actions): -- for action in actions: -- self.add_argument(action) -- -- # ======================= -- # Help-formatting methods -- # ======================= -- def format_help(self): -- help = self._root_section.format_help() % dict(prog=self._prog) -- if help: -- help = self._long_break_matcher.sub('\n\n', help) -- help = help.strip('\n') + '\n' -- return help -- -- def _join_parts(self, part_strings): -- return ''.join([part -- for part in part_strings -- if part and part is not SUPPRESS]) -- -- def _format_usage(self, usage, actions, groups, prefix): -- if prefix is None: -- prefix = _('usage: ') -- -- # if no optionals or positionals are available, usage is just prog -- if usage is None and not actions: -- usage = '%(prog)s' -- -- # if optionals and positionals are available, calculate usage -- elif usage is None: -- usage = '%(prog)s' % dict(prog=self._prog) -- -- # split optionals from positionals -- optionals = [] -- positionals = [] -- for action in actions: -- if action.option_strings: -- optionals.append(action) -- else: -- positionals.append(action) -- -- # determine width of "usage: PROG" and width of text -- prefix_width = len(prefix) + len(usage) + 1 -- prefix_indent = self._current_indent + prefix_width -- text_width = self._width - self._current_indent -- -- # put them on one line if they're short enough -- format = self._format_actions_usage -- action_usage = format(optionals + positionals, groups) -- if prefix_width + len(action_usage) + 1 < text_width: -- usage = '%s %s' % (usage, action_usage) -- -- # if they're long, wrap optionals and positionals individually -- else: -- optional_usage = format(optionals, groups) -- positional_usage = format(positionals, groups) -- indent = ' ' * prefix_indent -- -- # usage is made of PROG, optionals and positionals -- parts = [usage, ' '] -- -- # options always get added right after PROG -- if optional_usage: -- parts.append(_textwrap.fill( -- optional_usage, text_width, -- initial_indent=indent, -- subsequent_indent=indent).lstrip()) -- -- # if there were options, put arguments on the next line -- # otherwise, start them right after PROG -- if positional_usage: -- part = _textwrap.fill( -- positional_usage, text_width, -- initial_indent=indent, -- subsequent_indent=indent).lstrip() -- if optional_usage: -- part = '\n' + indent + part -- parts.append(part) -- usage = ''.join(parts) -- -- # prefix with 'usage:' -- return '%s%s\n\n' % (prefix, usage) -- -- def _format_actions_usage(self, actions, groups): -- # find group indices and identify actions in groups -- group_actions = _set() -- inserts = {} -- for group in groups: -- try: -- start = actions.index(group._group_actions[0]) -- except ValueError: -- continue -- else: -- end = start + len(group._group_actions) -- if actions[start:end] == group._group_actions: -- for action in group._group_actions: -- group_actions.add(action) -- if not group.required: -- inserts[start] = '[' -- inserts[end] = ']' -- else: -- inserts[start] = '(' -- inserts[end] = ')' -- for i in range(start + 1, end): -- inserts[i] = '|' -- -- # collect all actions format strings -- parts = [] -- for i, action in enumerate(actions): -- -- # suppressed arguments are marked with None -- # remove | separators for suppressed arguments -- if action.help is SUPPRESS: -- parts.append(None) -- if inserts.get(i) == '|': -- inserts.pop(i) -- elif inserts.get(i + 1) == '|': -- inserts.pop(i + 1) -- -- # produce all arg strings -- elif not action.option_strings: -- part = self._format_args(action, action.dest) -- -- # if it's in a group, strip the outer [] -- if action in group_actions: -- if part[0] == '[' and part[-1] == ']': -- part = part[1:-1] -- -- # add the action string to the list -- parts.append(part) -- -- # produce the first way to invoke the option in brackets -- else: -- option_string = action.option_strings[0] -- -- # if the Optional doesn't take a value, format is: -- # -s or --long -- if action.nargs == 0: -- part = '%s' % option_string -- -- # if the Optional takes a value, format is: -- # -s ARGS or --long ARGS -- else: -- default = action.dest.upper() -- args_string = self._format_args(action, default) -- part = '%s %s' % (option_string, args_string) -- -- # make it look optional if it's not required or in a group -- if not action.required and action not in group_actions: -- part = '[%s]' % part -- -- # add the action string to the list -- parts.append(part) -- -- # insert things at the necessary indices -- for i in _sorted(inserts, reverse=True): -- parts[i:i] = [inserts[i]] -- -- # join all the action items with spaces -- text = ' '.join([item for item in parts if item is not None]) -- -- # clean up separators for mutually exclusive groups -- open = r'[\[(]' -- close = r'[\])]' -- text = _re.sub(r'(%s) ' % open, r'\1', text) -- text = _re.sub(r' (%s)' % close, r'\1', text) -- text = _re.sub(r'%s *%s' % (open, close), r'', text) -- text = _re.sub(r'\(([^|]*)\)', r'\1', text) -- text = text.strip() -- -- # return the text -- return text -- -- def _format_text(self, text): -- text_width = self._width - self._current_indent -- indent = ' ' * self._current_indent -- return self._fill_text(text, text_width, indent) + '\n\n' -- -- def _format_action(self, action): -- # determine the required width and the entry label -- help_position = min(self._action_max_length + 2, -- self._max_help_position) -- help_width = self._width - help_position -- action_width = help_position - self._current_indent - 2 -- action_header = self._format_action_invocation(action) -- -- # ho nelp; start on same line and add a final newline -- if not action.help: -- tup = self._current_indent, '', action_header -- action_header = '%*s%s\n' % tup -- -- # short action name; start on the same line and pad two spaces -- elif len(action_header) <= action_width: -- tup = self._current_indent, '', action_width, action_header -- action_header = '%*s%-*s ' % tup -- indent_first = 0 -- -- # long action name; start on the next line -- else: -- tup = self._current_indent, '', action_header -- action_header = '%*s%s\n' % tup -- indent_first = help_position -- -- # collect the pieces of the action help -- parts = [action_header] -- -- # if there was help for the action, add lines of help text -- if action.help: -- help_text = self._expand_help(action) -- help_lines = self._split_lines(help_text, help_width) -- parts.append('%*s%s\n' % (indent_first, '', help_lines[0])) -- for line in help_lines[1:]: -- parts.append('%*s%s\n' % (help_position, '', line)) -- -- # or add a newline if the description doesn't end with one -- elif not action_header.endswith('\n'): -- parts.append('\n') -- -- # if there are any sub-actions, add their help as well -- for subaction in self._iter_indented_subactions(action): -- parts.append(self._format_action(subaction)) -- -- # return a single string -- return self._join_parts(parts) -- -- def _format_action_invocation(self, action): -- if not action.option_strings: -- metavar, = self._metavar_formatter(action, action.dest)(1) -- return metavar -- -- else: -- parts = [] -- -- # if the Optional doesn't take a value, format is: -- # -s, --long -- if action.nargs == 0: -- parts.extend(action.option_strings) -- -- # if the Optional takes a value, format is: -- # -s ARGS, --long ARGS -- else: -- default = action.dest.upper() -- args_string = self._format_args(action, default) -- for option_string in action.option_strings: -- parts.append('%s %s' % (option_string, args_string)) -- -- return ', '.join(parts) -- -- def _metavar_formatter(self, action, default_metavar): -- if action.metavar is not None: -- result = action.metavar -- elif action.choices is not None: -- choice_strs = [str(choice) for choice in action.choices] -- result = '{%s}' % ','.join(choice_strs) -- else: -- result = default_metavar -- -- def format(tuple_size): -- if isinstance(result, tuple): -- return result -- else: -- return (result, ) * tuple_size -- return format -- -- def _format_args(self, action, default_metavar): -- get_metavar = self._metavar_formatter(action, default_metavar) -- if action.nargs is None: -- result = '%s' % get_metavar(1) -- elif action.nargs == OPTIONAL: -- result = '[%s]' % get_metavar(1) -- elif action.nargs == ZERO_OR_MORE: -- result = '[%s [%s ...]]' % get_metavar(2) -- elif action.nargs == ONE_OR_MORE: -- result = '%s [%s ...]' % get_metavar(2) -- elif action.nargs is PARSER: -- result = '%s ...' % get_metavar(1) -- else: -- formats = ['%s' for _ in range(action.nargs)] -- result = ' '.join(formats) % get_metavar(action.nargs) -- return result -- -- def _expand_help(self, action): -- params = dict(vars(action), prog=self._prog) -- for name in list(params): -- if params[name] is SUPPRESS: -- del params[name] -- if params.get('choices') is not None: -- choices_str = ', '.join([str(c) for c in params['choices']]) -- params['choices'] = choices_str -- return self._get_help_string(action) % params -- -- def _iter_indented_subactions(self, action): -- try: -- get_subactions = action._get_subactions -- except AttributeError: -- pass -- else: -- self._indent() -- for subaction in get_subactions(): -- yield subaction -- self._dedent() -- -- def _split_lines(self, text, width): -- text = self._whitespace_matcher.sub(' ', text).strip() -- return _textwrap.wrap(text, width) -- -- def _fill_text(self, text, width, indent): -- text = self._whitespace_matcher.sub(' ', text).strip() -- return _textwrap.fill(text, width, initial_indent=indent, -- subsequent_indent=indent) -- -- def _get_help_string(self, action): -- return action.help -- -- --class RawDescriptionHelpFormatter(HelpFormatter): -- """Help message formatter which retains any formatting in descriptions. -- -- Only the name of this class is considered a public API. All the methods -- provided by the class are considered an implementation detail. -- """ -- -- def _fill_text(self, text, width, indent): -- return ''.join([indent + line for line in text.splitlines(True)]) -- -- --class RawTextHelpFormatter(RawDescriptionHelpFormatter): -- """Help message formatter which retains formatting of all help text. -- -- Only the name of this class is considered a public API. All the methods -- provided by the class are considered an implementation detail. -- """ -- -- def _split_lines(self, text, width): -- return text.splitlines() -- -- --class ArgumentDefaultsHelpFormatter(HelpFormatter): -- """Help message formatter which adds default values to argument help. -- -- Only the name of this class is considered a public API. All the methods -- provided by the class are considered an implementation detail. -- """ -- -- def _get_help_string(self, action): -- help = action.help -- if '%(default)' not in action.help: -- if action.default is not SUPPRESS: -- defaulting_nargs = [OPTIONAL, ZERO_OR_MORE] -- if action.option_strings or action.nargs in defaulting_nargs: -- help += ' (default: %(default)s)' -- return help -- -- --# ===================== --# Options and Arguments --# ===================== -- --def _get_action_name(argument): -- if argument is None: -- return None -- elif argument.option_strings: -- return '/'.join(argument.option_strings) -- elif argument.metavar not in (None, SUPPRESS): -- return argument.metavar -- elif argument.dest not in (None, SUPPRESS): -- return argument.dest -- else: -- return None -- -- --class ArgumentError(Exception): -- """An error from creating or using an argument (optional or positional). -- -- The string value of this exception is the message, augmented with -- information about the argument that caused it. -- """ -- -- def __init__(self, argument, message): -- self.argument_name = _get_action_name(argument) -- self.message = message -- -- def __str__(self): -- if self.argument_name is None: -- format = '%(message)s' -- else: -- format = 'argument %(argument_name)s: %(message)s' -- return format % dict(message=self.message, -- argument_name=self.argument_name) -- --# ============== --# Action classes --# ============== -- --class Action(_AttributeHolder): -- """Information about how to convert command line strings to Python objects. -- -- Action objects are used by an ArgumentParser to represent the information -- needed to parse a single argument from one or more strings from the -- command line. The keyword arguments to the Action constructor are also -- all attributes of Action instances. -- -- Keyword Arguments: -- -- - option_strings -- A list of command-line option strings which -- should be associated with this action. -- -- - dest -- The name of the attribute to hold the created object(s) -- -- - nargs -- The number of command-line arguments that should be -- consumed. By default, one argument will be consumed and a single -- value will be produced. Other values include: -- - N (an integer) consumes N arguments (and produces a list) -- - '?' consumes zero or one arguments -- - '*' consumes zero or more arguments (and produces a list) -- - '+' consumes one or more arguments (and produces a list) -- Note that the difference between the default and nargs=1 is that -- with the default, a single value will be produced, while with -- nargs=1, a list containing a single value will be produced. -- -- - const -- The value to be produced if the option is specified and the -- option uses an action that takes no values. -- -- - default -- The value to be produced if the option is not specified. -- -- - type -- The type which the command-line arguments should be converted -- to, should be one of 'string', 'int', 'float', 'complex' or a -- callable object that accepts a single string argument. If None, -- 'string' is assumed. -- -- - choices -- A container of values that should be allowed. If not None, -- after a command-line argument has been converted to the appropriate -- type, an exception will be raised if it is not a member of this -- collection. -- -- - required -- True if the action must always be specified at the -- command line. This is only meaningful for optional command-line -- arguments. -- -- - help -- The help string describing the argument. -- -- - metavar -- The name to be used for the option's argument with the -- help string. If None, the 'dest' value will be used as the name. -- """ -- -- def __init__(self, -- option_strings, -- dest, -- nargs=None, -- const=None, -- default=None, -- type=None, -- choices=None, -- required=False, -- help=None, -- metavar=None): -- self.option_strings = option_strings -- self.dest = dest -- self.nargs = nargs -- self.const = const -- self.default = default -- self.type = type -- self.choices = choices -- self.required = required -- self.help = help -- self.metavar = metavar -- -- def _get_kwargs(self): -- names = [ -- 'option_strings', -- 'dest', -- 'nargs', -- 'const', -- 'default', -- 'type', -- 'choices', -- 'help', -- 'metavar', -- ] -- return [(name, getattr(self, name)) for name in names] -- -- def __call__(self, parser, namespace, values, option_string=None): -- raise NotImplementedError(_('.__call__() not defined')) -- -- --class _StoreAction(Action): -- -- def __init__(self, -- option_strings, -- dest, -- nargs=None, -- const=None, -- default=None, -- type=None, -- choices=None, -- required=False, -- help=None, -- metavar=None): -- if nargs == 0: -- raise ValueError('nargs must be > 0') -- if const is not None and nargs != OPTIONAL: -- raise ValueError('nargs must be %r to supply const' % OPTIONAL) -- super(_StoreAction, self).__init__( -- option_strings=option_strings, -- dest=dest, -- nargs=nargs, -- const=const, -- default=default, -- type=type, -- choices=choices, -- required=required, -- help=help, -- metavar=metavar) -- -- def __call__(self, parser, namespace, values, option_string=None): -- setattr(namespace, self.dest, values) -- -- --class _StoreConstAction(Action): -- -- def __init__(self, -- option_strings, -- dest, -- const, -- default=None, -- required=False, -- help=None, -- metavar=None): -- super(_StoreConstAction, self).__init__( -- option_strings=option_strings, -- dest=dest, -- nargs=0, -- const=const, -- default=default, -- required=required, -- help=help) -- -- def __call__(self, parser, namespace, values, option_string=None): -- setattr(namespace, self.dest, self.const) -- -- --class _StoreTrueAction(_StoreConstAction): -- -- def __init__(self, -- option_strings, -- dest, -- default=False, -- required=False, -- help=None): -- super(_StoreTrueAction, self).__init__( -- option_strings=option_strings, -- dest=dest, -- const=True, -- default=default, -- required=required, -- help=help) -- -- --class _StoreFalseAction(_StoreConstAction): -- -- def __init__(self, -- option_strings, -- dest, -- default=True, -- required=False, -- help=None): -- super(_StoreFalseAction, self).__init__( -- option_strings=option_strings, -- dest=dest, -- const=False, -- default=default, -- required=required, -- help=help) -- -- --class _AppendAction(Action): -- -- def __init__(self, -- option_strings, -- dest, -- nargs=None, -- const=None, -- default=None, -- type=None, -- choices=None, -- required=False, -- help=None, -- metavar=None): -- if nargs == 0: -- raise ValueError('nargs must be > 0') -- if const is not None and nargs != OPTIONAL: -- raise ValueError('nargs must be %r to supply const' % OPTIONAL) -- super(_AppendAction, self).__init__( -- option_strings=option_strings, -- dest=dest, -- nargs=nargs, -- const=const, -- default=default, -- type=type, -- choices=choices, -- required=required, -- help=help, -- metavar=metavar) -- -- def __call__(self, parser, namespace, values, option_string=None): -- items = _copy.copy(_ensure_value(namespace, self.dest, [])) -- items.append(values) -- setattr(namespace, self.dest, items) -- -- --class _AppendConstAction(Action): -- -- def __init__(self, -- option_strings, -- dest, -- const, -- default=None, -- required=False, -- help=None, -- metavar=None): -- super(_AppendConstAction, self).__init__( -- option_strings=option_strings, -- dest=dest, -- nargs=0, -- const=const, -- default=default, -- required=required, -- help=help, -- metavar=metavar) -- -- def __call__(self, parser, namespace, values, option_string=None): -- items = _copy.copy(_ensure_value(namespace, self.dest, [])) -- items.append(self.const) -- setattr(namespace, self.dest, items) -- -- --class _CountAction(Action): -- -- def __init__(self, -- option_strings, -- dest, -- default=None, -- required=False, -- help=None): -- super(_CountAction, self).__init__( -- option_strings=option_strings, -- dest=dest, -- nargs=0, -- default=default, -- required=required, -- help=help) -- -- def __call__(self, parser, namespace, values, option_string=None): -- new_count = _ensure_value(namespace, self.dest, 0) + 1 -- setattr(namespace, self.dest, new_count) -- -- --class _HelpAction(Action): -- -- def __init__(self, -- option_strings, -- dest=SUPPRESS, -- default=SUPPRESS, -- help=None): -- super(_HelpAction, self).__init__( -- option_strings=option_strings, -- dest=dest, -- default=default, -- nargs=0, -- help=help) -- -- def __call__(self, parser, namespace, values, option_string=None): -- parser.print_help() -- parser.exit() -- -- --class _VersionAction(Action): -- -- def __init__(self, -- option_strings, -- dest=SUPPRESS, -- default=SUPPRESS, -- help=None): -- super(_VersionAction, self).__init__( -- option_strings=option_strings, -- dest=dest, -- default=default, -- nargs=0, -- help=help) -- -- def __call__(self, parser, namespace, values, option_string=None): -- parser.print_version() -- parser.exit() -- -- --class _SubParsersAction(Action): -- -- class _ChoicesPseudoAction(Action): -- -- def __init__(self, name, help): -- sup = super(_SubParsersAction._ChoicesPseudoAction, self) -- sup.__init__(option_strings=[], dest=name, help=help) -- -- def __init__(self, -- option_strings, -- prog, -- parser_class, -- dest=SUPPRESS, -- help=None, -- metavar=None): -- -- self._prog_prefix = prog -- self._parser_class = parser_class -- self._name_parser_map = {} -- self._choices_actions = [] -- -- super(_SubParsersAction, self).__init__( -- option_strings=option_strings, -- dest=dest, -- nargs=PARSER, -- choices=self._name_parser_map, -- help=help, -- metavar=metavar) -- -- def add_parser(self, name, **kwargs): -- # set prog from the existing prefix -- if kwargs.get('prog') is None: -- kwargs['prog'] = '%s %s' % (self._prog_prefix, name) -- -- # create a pseudo-action to hold the choice help -- if 'help' in kwargs: -- help = kwargs.pop('help') -- choice_action = self._ChoicesPseudoAction(name, help) -- self._choices_actions.append(choice_action) -- -- # create the parser and add it to the map -- parser = self._parser_class(**kwargs) -- self._name_parser_map[name] = parser -- return parser -- -- def _get_subactions(self): -- return self._choices_actions -- -- def __call__(self, parser, namespace, values, option_string=None): -- parser_name = values[0] -- arg_strings = values[1:] -- -- # set the parser name if requested -- if self.dest is not SUPPRESS: -- setattr(namespace, self.dest, parser_name) -- -- # select the parser -- try: -- parser = self._name_parser_map[parser_name] -- except KeyError: -- tup = parser_name, ', '.join(self._name_parser_map) -- msg = _('unknown parser %r (choices: %s)' % tup) -- raise ArgumentError(self, msg) -- -- # parse all the remaining options into the namespace -- parser.parse_args(arg_strings, namespace) -- -- --# ============== --# Type classes --# ============== -- --class FileType(object): -- """Factory for creating file object types -- -- Instances of FileType are typically passed as type= arguments to the -- ArgumentParser add_argument() method. -- -- Keyword Arguments: -- - mode -- A string indicating how the file is to be opened. Accepts the -- same values as the builtin open() function. -- - bufsize -- The file's desired buffer size. Accepts the same values as -- the builtin open() function. -- """ -- -- def __init__(self, mode='r', bufsize=None): -- self._mode = mode -- self._bufsize = bufsize -- -- def __call__(self, string): -- # the special argument "-" means sys.std{in,out} -- if string == '-': -- if 'r' in self._mode: -- return _sys.stdin -- elif 'w' in self._mode: -- return _sys.stdout -- else: -- msg = _('argument "-" with mode %r' % self._mode) -- raise ValueError(msg) -- -- # all other arguments are used as file names -- if self._bufsize: -- return open(string, self._mode, self._bufsize) -- else: -- return open(string, self._mode) -- -- def __repr__(self): -- args = [self._mode, self._bufsize] -- args_str = ', '.join([repr(arg) for arg in args if arg is not None]) -- return '%s(%s)' % (type(self).__name__, args_str) -- --# =========================== --# Optional and Positional Parsing --# =========================== -- --class Namespace(_AttributeHolder): -- """Simple object for storing attributes. -- -- Implements equality by attribute names and values, and provides a simple -- string representation. -- """ -- -- def __init__(self, **kwargs): -- for name in kwargs: -- setattr(self, name, kwargs[name]) -- -- def __eq__(self, other): -- return vars(self) == vars(other) -- -- def __ne__(self, other): -- return not (self == other) -- -- --class _ActionsContainer(object): -- -- def __init__(self, -- description, -- prefix_chars, -- argument_default, -- conflict_handler): -- super(_ActionsContainer, self).__init__() -- -- self.description = description -- self.argument_default = argument_default -- self.prefix_chars = prefix_chars -- self.conflict_handler = conflict_handler -- -- # set up registries -- self._registries = {} -- -- # register actions -- self.register('action', None, _StoreAction) -- self.register('action', 'store', _StoreAction) -- self.register('action', 'store_const', _StoreConstAction) -- self.register('action', 'store_true', _StoreTrueAction) -- self.register('action', 'store_false', _StoreFalseAction) -- self.register('action', 'append', _AppendAction) -- self.register('action', 'append_const', _AppendConstAction) -- self.register('action', 'count', _CountAction) -- self.register('action', 'help', _HelpAction) -- self.register('action', 'version', _VersionAction) -- self.register('action', 'parsers', _SubParsersAction) -- -- # raise an exception if the conflict handler is invalid -- self._get_handler() -- -- # action storage -- self._actions = [] -- self._option_string_actions = {} -- -- # groups -- self._action_groups = [] -- self._mutually_exclusive_groups = [] -- -- # defaults storage -- self._defaults = {} -- -- # determines whether an "option" looks like a negative number -- self._negative_number_matcher = _re.compile(r'^-\d+|-\d*.\d+$') -- -- # whether or not there are any optionals that look like negative -- # numbers -- uses a list so it can be shared and edited -- self._has_negative_number_optionals = [] -- -- # ==================== -- # Registration methods -- # ==================== -- def register(self, registry_name, value, object): -- registry = self._registries.setdefault(registry_name, {}) -- registry[value] = object -- -- def _registry_get(self, registry_name, value, default=None): -- return self._registries[registry_name].get(value, default) -- -- # ================================== -- # Namespace default settings methods -- # ================================== -- def set_defaults(self, **kwargs): -- self._defaults.update(kwargs) -- -- # if these defaults match any existing arguments, replace -- # the previous default on the object with the new one -- for action in self._actions: -- if action.dest in kwargs: -- action.default = kwargs[action.dest] -- -- # ======================= -- # Adding argument actions -- # ======================= -- def add_argument(self, *args, **kwargs): -- """ -- add_argument(dest, ..., name=value, ...) -- add_argument(option_string, option_string, ..., name=value, ...) -- """ -- -- # if no positional args are supplied or only one is supplied and -- # it doesn't look like an option string, parse a positional -- # argument -- chars = self.prefix_chars -- if not args or len(args) == 1 and args[0][0] not in chars: -- kwargs = self._get_positional_kwargs(*args, **kwargs) -- -- # otherwise, we're adding an optional argument -- else: -- kwargs = self._get_optional_kwargs(*args, **kwargs) -- -- # if no default was supplied, use the parser-level default -- if 'default' not in kwargs: -- dest = kwargs['dest'] -- if dest in self._defaults: -- kwargs['default'] = self._defaults[dest] -- elif self.argument_default is not None: -- kwargs['default'] = self.argument_default -- -- # create the action object, and add it to the parser -- action_class = self._pop_action_class(kwargs) -- action = action_class(**kwargs) -- return self._add_action(action) -- -- def add_argument_group(self, *args, **kwargs): -- group = _ArgumentGroup(self, *args, **kwargs) -- self._action_groups.append(group) -- return group -- -- def add_mutually_exclusive_group(self, **kwargs): -- group = _MutuallyExclusiveGroup(self, **kwargs) -- self._mutually_exclusive_groups.append(group) -- return group -- -- def _add_action(self, action): -- # resolve any conflicts -- self._check_conflict(action) -- -- # add to actions list -- self._actions.append(action) -- action.container = self -- -- # index the action by any option strings it has -- for option_string in action.option_strings: -- self._option_string_actions[option_string] = action -- -- # set the flag if any option strings look like negative numbers -- for option_string in action.option_strings: -- if self._negative_number_matcher.match(option_string): -- if not self._has_negative_number_optionals: -- self._has_negative_number_optionals.append(True) -- -- # return the created action -- return action -- -- def _remove_action(self, action): -- self._actions.remove(action) -- -- def _add_container_actions(self, container): -- # collect groups by titles -- title_group_map = {} -- for group in self._action_groups: -- if group.title in title_group_map: -- msg = _('cannot merge actions - two groups are named %r') -- raise ValueError(msg % (group.title)) -- title_group_map[group.title] = group -- -- # map each action to its group -- group_map = {} -- for group in container._action_groups: -- -- # if a group with the title exists, use that, otherwise -- # create a new group matching the container's group -- if group.title not in title_group_map: -- title_group_map[group.title] = self.add_argument_group( -- title=group.title, -- description=group.description, -- conflict_handler=group.conflict_handler) -- -- # map the actions to their new group -- for action in group._group_actions: -- group_map[action] = title_group_map[group.title] -- -- # add all actions to this container or their group -- for action in container._actions: -- group_map.get(action, self)._add_action(action) -- -- def _get_positional_kwargs(self, dest, **kwargs): -- # make sure required is not specified -- if 'required' in kwargs: -- msg = _("'required' is an invalid argument for positionals") -- raise TypeError(msg) -- -- # mark positional arguments as required if at least one is -- # always required -- if kwargs.get('nargs') not in [OPTIONAL, ZERO_OR_MORE]: -- kwargs['required'] = True -- if kwargs.get('nargs') == ZERO_OR_MORE and 'default' not in kwargs: -- kwargs['required'] = True -- -- # return the keyword arguments with no option strings -- return dict(kwargs, dest=dest, option_strings=[]) -- -- def _get_optional_kwargs(self, *args, **kwargs): -- # determine short and long option strings -- option_strings = [] -- long_option_strings = [] -- for option_string in args: -- # error on one-or-fewer-character option strings -- if len(option_string) < 2: -- msg = _('invalid option string %r: ' -- 'must be at least two characters long') -- raise ValueError(msg % option_string) -- -- # error on strings that don't start with an appropriate prefix -- if not option_string[0] in self.prefix_chars: -- msg = _('invalid option string %r: ' -- 'must start with a character %r') -- tup = option_string, self.prefix_chars -- raise ValueError(msg % tup) -- -- # error on strings that are all prefix characters -- if not (_set(option_string) - _set(self.prefix_chars)): -- msg = _('invalid option string %r: ' -- 'must contain characters other than %r') -- tup = option_string, self.prefix_chars -- raise ValueError(msg % tup) -- -- # strings starting with two prefix characters are long options -- option_strings.append(option_string) -- if option_string[0] in self.prefix_chars: -- if option_string[1] in self.prefix_chars: -- long_option_strings.append(option_string) -- -- # infer destination, '--foo-bar' -> 'foo_bar' and '-x' -> 'x' -- dest = kwargs.pop('dest', None) -- if dest is None: -- if long_option_strings: -- dest_option_string = long_option_strings[0] -- else: -- dest_option_string = option_strings[0] -- dest = dest_option_string.lstrip(self.prefix_chars) -- dest = dest.replace('-', '_') -- -- # return the updated keyword arguments -- return dict(kwargs, dest=dest, option_strings=option_strings) -- -- def _pop_action_class(self, kwargs, default=None): -- action = kwargs.pop('action', default) -- return self._registry_get('action', action, action) -- -- def _get_handler(self): -- # determine function from conflict handler string -- handler_func_name = '_handle_conflict_%s' % self.conflict_handler -- try: -- return getattr(self, handler_func_name) -- except AttributeError: -- msg = _('invalid conflict_resolution value: %r') -- raise ValueError(msg % self.conflict_handler) -- -- def _check_conflict(self, action): -- -- # find all options that conflict with this option -- confl_optionals = [] -- for option_string in action.option_strings: -- if option_string in self._option_string_actions: -- confl_optional = self._option_string_actions[option_string] -- confl_optionals.append((option_string, confl_optional)) -- -- # resolve any conflicts -- if confl_optionals: -- conflict_handler = self._get_handler() -- conflict_handler(action, confl_optionals) -- -- def _handle_conflict_error(self, action, conflicting_actions): -- message = _('conflicting option string(s): %s') -- conflict_string = ', '.join([option_string -- for option_string, action -- in conflicting_actions]) -- raise ArgumentError(action, message % conflict_string) -- -- def _handle_conflict_resolve(self, action, conflicting_actions): -- -- # remove all conflicting options -- for option_string, action in conflicting_actions: -- -- # remove the conflicting option -- action.option_strings.remove(option_string) -- self._option_string_actions.pop(option_string, None) -- -- # if the option now has no option string, remove it from the -- # container holding it -- if not action.option_strings: -- action.container._remove_action(action) -- -- --class _ArgumentGroup(_ActionsContainer): -- -- def __init__(self, container, title=None, description=None, **kwargs): -- # add any missing keyword arguments by checking the container -- update = kwargs.setdefault -- update('conflict_handler', container.conflict_handler) -- update('prefix_chars', container.prefix_chars) -- update('argument_default', container.argument_default) -- super_init = super(_ArgumentGroup, self).__init__ -- super_init(description=description, **kwargs) -- -- # group attributes -- self.title = title -- self._group_actions = [] -- -- # share most attributes with the container -- self._registries = container._registries -- self._actions = container._actions -- self._option_string_actions = container._option_string_actions -- self._defaults = container._defaults -- self._has_negative_number_optionals = \ -- container._has_negative_number_optionals -- -- def _add_action(self, action): -- action = super(_ArgumentGroup, self)._add_action(action) -- self._group_actions.append(action) -- return action -- -- def _remove_action(self, action): -- super(_ArgumentGroup, self)._remove_action(action) -- self._group_actions.remove(action) -- -- --class _MutuallyExclusiveGroup(_ArgumentGroup): -- -- def __init__(self, container, required=False): -- super(_MutuallyExclusiveGroup, self).__init__(container) -- self.required = required -- self._container = container -- -- def _add_action(self, action): -- if action.required: -- msg = _('mutually exclusive arguments must be optional') -- raise ValueError(msg) -- action = self._container._add_action(action) -- self._group_actions.append(action) -- return action -- -- def _remove_action(self, action): -- self._container._remove_action(action) -- self._group_actions.remove(action) -- -- --class ArgumentParser(_AttributeHolder, _ActionsContainer): -- """Object for parsing command line strings into Python objects. -- -- Keyword Arguments: -- - prog -- The name of the program (default: sys.argv[0]) -- - usage -- A usage message (default: auto-generated from arguments) -- - description -- A description of what the program does -- - epilog -- Text following the argument descriptions -- - version -- Add a -v/--version option with the given version string -- - parents -- Parsers whose arguments should be copied into this one -- - formatter_class -- HelpFormatter class for printing help messages -- - prefix_chars -- Characters that prefix optional arguments -- - fromfile_prefix_chars -- Characters that prefix files containing -- additional arguments -- - argument_default -- The default value for all arguments -- - conflict_handler -- String indicating how to handle conflicts -- - add_help -- Add a -h/-help option -- """ -- -- def __init__(self, -- prog=None, -- usage=None, -- description=None, -- epilog=None, -- version=None, -- parents=[], -- formatter_class=HelpFormatter, -- prefix_chars='-', -- fromfile_prefix_chars=None, -- argument_default=None, -- conflict_handler='error', -- add_help=True): -- -- superinit = super(ArgumentParser, self).__init__ -- superinit(description=description, -- prefix_chars=prefix_chars, -- argument_default=argument_default, -- conflict_handler=conflict_handler) -- -- # default setting for prog -- if prog is None: -- prog = _os.path.basename(_sys.argv[0]) -- -- self.prog = prog -- self.usage = usage -- self.epilog = epilog -- self.version = version -- self.formatter_class = formatter_class -- self.fromfile_prefix_chars = fromfile_prefix_chars -- self.add_help = add_help -- -- add_group = self.add_argument_group -- self._positionals = add_group(_('positional arguments')) -- self._optionals = add_group(_('optional arguments')) -- self._subparsers = None -- -- # register types -- def identity(string): -- return string -- self.register('type', None, identity) -- -- # add help and version arguments if necessary -- # (using explicit default to override global argument_default) -- if self.add_help: -- self.add_argument( -- '-h', '--help', action='help', default=SUPPRESS, -- help=_('show this help message and exit')) -- if self.version: -- self.add_argument( -- '-v', '--version', action='version', default=SUPPRESS, -- help=_("show program's version number and exit")) -- -- # add parent arguments and defaults -- for parent in parents: -- self._add_container_actions(parent) -- try: -- defaults = parent._defaults -- except AttributeError: -- pass -- else: -- self._defaults.update(defaults) -- -- # ======================= -- # Pretty __repr__ methods -- # ======================= -- def _get_kwargs(self): -- names = [ -- 'prog', -- 'usage', -- 'description', -- 'version', -- 'formatter_class', -- 'conflict_handler', -- 'add_help', -- ] -- return [(name, getattr(self, name)) for name in names] -- -- # ================================== -- # Optional/Positional adding methods -- # ================================== -- def add_subparsers(self, **kwargs): -- if self._subparsers is not None: -- self.error(_('cannot have multiple subparser arguments')) -- -- # add the parser class to the arguments if it's not present -- kwargs.setdefault('parser_class', type(self)) -- -- if 'title' in kwargs or 'description' in kwargs: -- title = _(kwargs.pop('title', 'subcommands')) -- description = _(kwargs.pop('description', None)) -- self._subparsers = self.add_argument_group(title, description) -- else: -- self._subparsers = self._positionals -- -- # prog defaults to the usage message of this parser, skipping -- # optional arguments and with no "usage:" prefix -- if kwargs.get('prog') is None: -- formatter = self._get_formatter() -- positionals = self._get_positional_actions() -- groups = self._mutually_exclusive_groups -- formatter.add_usage(self.usage, positionals, groups, '') -- kwargs['prog'] = formatter.format_help().strip() -- -- # create the parsers action and add it to the positionals list -- parsers_class = self._pop_action_class(kwargs, 'parsers') -- action = parsers_class(option_strings=[], **kwargs) -- self._subparsers._add_action(action) -- -- # return the created parsers action -- return action -- -- def _add_action(self, action): -- if action.option_strings: -- self._optionals._add_action(action) -- else: -- self._positionals._add_action(action) -- return action -- -- def _get_optional_actions(self): -- return [action -- for action in self._actions -- if action.option_strings] -- -- def _get_positional_actions(self): -- return [action -- for action in self._actions -- if not action.option_strings] -- -- # ===================================== -- # Command line argument parsing methods -- # ===================================== -- def parse_args(self, args=None, namespace=None): -- args, argv = self.parse_known_args(args, namespace) -- if argv: -- msg = _('unrecognized arguments: %s') -- self.error(msg % ' '.join(argv)) -- return args -- -- def parse_known_args(self, args=None, namespace=None): -- # args default to the system args -- if args is None: -- args = _sys.argv[1:] -- -- # default Namespace built from parser defaults -- if namespace is None: -- namespace = Namespace() -- -- # add any action defaults that aren't present -- for action in self._actions: -- if action.dest is not SUPPRESS: -- if not hasattr(namespace, action.dest): -- if action.default is not SUPPRESS: -- default = action.default -- if isinstance(action.default, _basestring): -- default = self._get_value(action, default) -- setattr(namespace, action.dest, default) -- -- # add any parser defaults that aren't present -- for dest in self._defaults: -- if not hasattr(namespace, dest): -- setattr(namespace, dest, self._defaults[dest]) -- -- # parse the arguments and exit if there are any errors -- try: -- return self._parse_known_args(args, namespace) -- except ArgumentError: -- err = _sys.exc_info()[1] -- self.error(str(err)) -- -- def _parse_known_args(self, arg_strings, namespace): -- # replace arg strings that are file references -- if self.fromfile_prefix_chars is not None: -- arg_strings = self._read_args_from_files(arg_strings) -- -- # map all mutually exclusive arguments to the other arguments -- # they can't occur with -- action_conflicts = {} -- for mutex_group in self._mutually_exclusive_groups: -- group_actions = mutex_group._group_actions -- for i, mutex_action in enumerate(mutex_group._group_actions): -- conflicts = action_conflicts.setdefault(mutex_action, []) -- conflicts.extend(group_actions[:i]) -- conflicts.extend(group_actions[i + 1:]) -- -- # find all option indices, and determine the arg_string_pattern -- # which has an 'O' if there is an option at an index, -- # an 'A' if there is an argument, or a '-' if there is a '--' -- option_string_indices = {} -- arg_string_pattern_parts = [] -- arg_strings_iter = iter(arg_strings) -- for i, arg_string in enumerate(arg_strings_iter): -- -- # all args after -- are non-options -- if arg_string == '--': -- arg_string_pattern_parts.append('-') -- for arg_string in arg_strings_iter: -- arg_string_pattern_parts.append('A') -- -- # otherwise, add the arg to the arg strings -- # and note the index if it was an option -- else: -- option_tuple = self._parse_optional(arg_string) -- if option_tuple is None: -- pattern = 'A' -- else: -- option_string_indices[i] = option_tuple -- pattern = 'O' -- arg_string_pattern_parts.append(pattern) -- -- # join the pieces together to form the pattern -- arg_strings_pattern = ''.join(arg_string_pattern_parts) -- -- # converts arg strings to the appropriate and then takes the action -- seen_actions = _set() -- seen_non_default_actions = _set() -- -- def take_action(action, argument_strings, option_string=None): -- seen_actions.add(action) -- argument_values = self._get_values(action, argument_strings) -- -- # error if this argument is not allowed with other previously -- # seen arguments, assuming that actions that use the default -- # value don't really count as "present" -- if argument_values is not action.default: -- seen_non_default_actions.add(action) -- for conflict_action in action_conflicts.get(action, []): -- if conflict_action in seen_non_default_actions: -- msg = _('not allowed with argument %s') -- action_name = _get_action_name(conflict_action) -- raise ArgumentError(action, msg % action_name) -- -- # take the action if we didn't receive a SUPPRESS value -- # (e.g. from a default) -- if argument_values is not SUPPRESS: -- action(self, namespace, argument_values, option_string) -- -- # function to convert arg_strings into an optional action -- def consume_optional(start_index): -- -- # get the optional identified at this index -- option_tuple = option_string_indices[start_index] -- action, option_string, explicit_arg = option_tuple -- -- # identify additional optionals in the same arg string -- # (e.g. -xyz is the same as -x -y -z if no args are required) -- match_argument = self._match_argument -- action_tuples = [] -- while True: -- -- # if we found no optional action, skip it -- if action is None: -- extras.append(arg_strings[start_index]) -- return start_index + 1 -- -- # if there is an explicit argument, try to match the -- # optional's string arguments to only this -- if explicit_arg is not None: -- arg_count = match_argument(action, 'A') -- -- # if the action is a single-dash option and takes no -- # arguments, try to parse more single-dash options out -- # of the tail of the option string -- chars = self.prefix_chars -- if arg_count == 0 and option_string[1] not in chars: -- action_tuples.append((action, [], option_string)) -- for char in self.prefix_chars: -- option_string = char + explicit_arg[0] -- explicit_arg = explicit_arg[1:] or None -- optionals_map = self._option_string_actions -- if option_string in optionals_map: -- action = optionals_map[option_string] -- break -- else: -- msg = _('ignored explicit argument %r') -- raise ArgumentError(action, msg % explicit_arg) -- -- # if the action expect exactly one argument, we've -- # successfully matched the option; exit the loop -- elif arg_count == 1: -- stop = start_index + 1 -- args = [explicit_arg] -- action_tuples.append((action, args, option_string)) -- break -- -- # error if a double-dash option did not use the -- # explicit argument -- else: -- msg = _('ignored explicit argument %r') -- raise ArgumentError(action, msg % explicit_arg) -- -- # if there is no explicit argument, try to match the -- # optional's string arguments with the following strings -- # if successful, exit the loop -- else: -- start = start_index + 1 -- selected_patterns = arg_strings_pattern[start:] -- arg_count = match_argument(action, selected_patterns) -- stop = start + arg_count -- args = arg_strings[start:stop] -- action_tuples.append((action, args, option_string)) -- break -- -- # add the Optional to the list and return the index at which -- # the Optional's string args stopped -- assert action_tuples -- for action, args, option_string in action_tuples: -- take_action(action, args, option_string) -- return stop -- -- # the list of Positionals left to be parsed; this is modified -- # by consume_positionals() -- positionals = self._get_positional_actions() -- -- # function to convert arg_strings into positional actions -- def consume_positionals(start_index): -- # match as many Positionals as possible -- match_partial = self._match_arguments_partial -- selected_pattern = arg_strings_pattern[start_index:] -- arg_counts = match_partial(positionals, selected_pattern) -- -- # slice off the appropriate arg strings for each Positional -- # and add the Positional and its args to the list -- for action, arg_count in zip(positionals, arg_counts): -- args = arg_strings[start_index: start_index + arg_count] -- start_index += arg_count -- take_action(action, args) -- -- # slice off the Positionals that we just parsed and return the -- # index at which the Positionals' string args stopped -- positionals[:] = positionals[len(arg_counts):] -- return start_index -- -- # consume Positionals and Optionals alternately, until we have -- # passed the last option string -- extras = [] -- start_index = 0 -- if option_string_indices: -- max_option_string_index = max(option_string_indices) -- else: -- max_option_string_index = -1 -- while start_index <= max_option_string_index: -- -- # consume any Positionals preceding the next option -- next_option_string_index = min([ -- index -- for index in option_string_indices -- if index >= start_index]) -- if start_index != next_option_string_index: -- positionals_end_index = consume_positionals(start_index) -- -- # only try to parse the next optional if we didn't consume -- # the option string during the positionals parsing -- if positionals_end_index > start_index: -- start_index = positionals_end_index -- continue -- else: -- start_index = positionals_end_index -- -- # if we consumed all the positionals we could and we're not -- # at the index of an option string, there were extra arguments -- if start_index not in option_string_indices: -- strings = arg_strings[start_index:next_option_string_index] -- extras.extend(strings) -- start_index = next_option_string_index -- -- # consume the next optional and any arguments for it -- start_index = consume_optional(start_index) -- -- # consume any positionals following the last Optional -- stop_index = consume_positionals(start_index) -- -- # if we didn't consume all the argument strings, there were extras -- extras.extend(arg_strings[stop_index:]) -- -- # if we didn't use all the Positional objects, there were too few -- # arg strings supplied. -- if positionals: -- self.error(_('too few arguments')) -- -- # make sure all required actions were present -- for action in self._actions: -- if action.required: -- if action not in seen_actions: -- name = _get_action_name(action) -- self.error(_('argument %s is required') % name) -- -- # make sure all required groups had one option present -- for group in self._mutually_exclusive_groups: -- if group.required: -- for action in group._group_actions: -- if action in seen_non_default_actions: -- break -- -- # if no actions were used, report the error -- else: -- names = [_get_action_name(action) -- for action in group._group_actions -- if action.help is not SUPPRESS] -- msg = _('one of the arguments %s is required') -- self.error(msg % ' '.join(names)) -- -- # return the updated namespace and the extra arguments -- return namespace, extras -- -- def _read_args_from_files(self, arg_strings): -- # expand arguments referencing files -- new_arg_strings = [] -- for arg_string in arg_strings: -- -- # for regular arguments, just add them back into the list -- if arg_string[0] not in self.fromfile_prefix_chars: -- new_arg_strings.append(arg_string) -- -- # replace arguments referencing files with the file content -- else: -- try: -- args_file = open(arg_string[1:]) -- try: -- arg_strings = args_file.read().splitlines() -- arg_strings = self._read_args_from_files(arg_strings) -- new_arg_strings.extend(arg_strings) -- finally: -- args_file.close() -- except IOError: -- err = _sys.exc_info()[1] -- self.error(str(err)) -- -- # return the modified argument list -- return new_arg_strings -- -- def _match_argument(self, action, arg_strings_pattern): -- # match the pattern for this action to the arg strings -- nargs_pattern = self._get_nargs_pattern(action) -- match = _re.match(nargs_pattern, arg_strings_pattern) -- -- # raise an exception if we weren't able to find a match -- if match is None: -- nargs_errors = { -- None: _('expected one argument'), -- OPTIONAL: _('expected at most one argument'), -- ONE_OR_MORE: _('expected at least one argument'), -- } -- default = _('expected %s argument(s)') % action.nargs -- msg = nargs_errors.get(action.nargs, default) -- raise ArgumentError(action, msg) -- -- # return the number of arguments matched -- return len(match.group(1)) -- -- def _match_arguments_partial(self, actions, arg_strings_pattern): -- # progressively shorten the actions list by slicing off the -- # final actions until we find a match -- result = [] -- for i in range(len(actions), 0, -1): -- actions_slice = actions[:i] -- pattern = ''.join([self._get_nargs_pattern(action) -- for action in actions_slice]) -- match = _re.match(pattern, arg_strings_pattern) -- if match is not None: -- result.extend([len(string) for string in match.groups()]) -- break -- -- # return the list of arg string counts -- return result -- -- def _parse_optional(self, arg_string): -- # if it's an empty string, it was meant to be a positional -- if not arg_string: -- return None -- -- # if it doesn't start with a prefix, it was meant to be positional -- if not arg_string[0] in self.prefix_chars: -- return None -- -- # if it's just dashes, it was meant to be positional -- if not arg_string.strip('-'): -- return None -- -- # if the option string is present in the parser, return the action -- if arg_string in self._option_string_actions: -- action = self._option_string_actions[arg_string] -- return action, arg_string, None -- -- # search through all possible prefixes of the option string -- # and all actions in the parser for possible interpretations -- option_tuples = self._get_option_tuples(arg_string) -- -- # if multiple actions match, the option string was ambiguous -- if len(option_tuples) > 1: -- options = ', '.join([option_string -- for action, option_string, explicit_arg in option_tuples]) -- tup = arg_string, options -- self.error(_('ambiguous option: %s could match %s') % tup) -- -- # if exactly one action matched, this segmentation is good, -- # so return the parsed action -- elif len(option_tuples) == 1: -- option_tuple, = option_tuples -- return option_tuple -- -- # if it was not found as an option, but it looks like a negative -- # number, it was meant to be positional -- # unless there are negative-number-like options -- if self._negative_number_matcher.match(arg_string): -- if not self._has_negative_number_optionals: -- return None -- -- # if it contains a space, it was meant to be a positional -- if ' ' in arg_string: -- return None -- -- # it was meant to be an optional but there is no such option -- # in this parser (though it might be a valid option in a subparser) -- return None, arg_string, None -- -- def _get_option_tuples(self, option_string): -- result = [] -- -- # option strings starting with two prefix characters are only -- # split at the '=' -- chars = self.prefix_chars -- if option_string[0] in chars and option_string[1] in chars: -- if '=' in option_string: -- option_prefix, explicit_arg = option_string.split('=', 1) -- else: -- option_prefix = option_string -- explicit_arg = None -- for option_string in self._option_string_actions: -- if option_string.startswith(option_prefix): -- action = self._option_string_actions[option_string] -- tup = action, option_string, explicit_arg -- result.append(tup) -- -- # single character options can be concatenated with their arguments -- # but multiple character options always have to have their argument -- # separate -- elif option_string[0] in chars and option_string[1] not in chars: -- option_prefix = option_string -- explicit_arg = None -- short_option_prefix = option_string[:2] -- short_explicit_arg = option_string[2:] -- -- for option_string in self._option_string_actions: -- if option_string == short_option_prefix: -- action = self._option_string_actions[option_string] -- tup = action, option_string, short_explicit_arg -- result.append(tup) -- elif option_string.startswith(option_prefix): -- action = self._option_string_actions[option_string] -- tup = action, option_string, explicit_arg -- result.append(tup) -- -- # shouldn't ever get here -- else: -- self.error(_('unexpected option string: %s') % option_string) -- -- # return the collected option tuples -- return result -- -- def _get_nargs_pattern(self, action): -- # in all examples below, we have to allow for '--' args -- # which are represented as '-' in the pattern -- nargs = action.nargs -- -- # the default (None) is assumed to be a single argument -- if nargs is None: -- nargs_pattern = '(-*A-*)' -- -- # allow zero or one arguments -- elif nargs == OPTIONAL: -- nargs_pattern = '(-*A?-*)' -- -- # allow zero or more arguments -- elif nargs == ZERO_OR_MORE: -- nargs_pattern = '(-*[A-]*)' -- -- # allow one or more arguments -- elif nargs == ONE_OR_MORE: -- nargs_pattern = '(-*A[A-]*)' -- -- # allow one argument followed by any number of options or arguments -- elif nargs is PARSER: -- nargs_pattern = '(-*A[-AO]*)' -- -- # all others should be integers -- else: -- nargs_pattern = '(-*%s-*)' % '-*'.join('A' * nargs) -- -- # if this is an optional action, -- is not allowed -- if action.option_strings: -- nargs_pattern = nargs_pattern.replace('-*', '') -- nargs_pattern = nargs_pattern.replace('-', '') -- -- # return the pattern -- return nargs_pattern -- -- # ======================== -- # Value conversion methods -- # ======================== -- def _get_values(self, action, arg_strings): -- # for everything but PARSER args, strip out '--' -- if action.nargs is not PARSER: -- arg_strings = [s for s in arg_strings if s != '--'] -- -- # optional argument produces a default when not present -- if not arg_strings and action.nargs == OPTIONAL: -- if action.option_strings: -- value = action.const -- else: -- value = action.default -- if isinstance(value, _basestring): -- value = self._get_value(action, value) -- self._check_value(action, value) -- -- # when nargs='*' on a positional, if there were no command-line -- # args, use the default if it is anything other than None -- elif (not arg_strings and action.nargs == ZERO_OR_MORE and -- not action.option_strings): -- if action.default is not None: -- value = action.default -- else: -- value = arg_strings -- self._check_value(action, value) -- -- # single argument or optional argument produces a single value -- elif len(arg_strings) == 1 and action.nargs in [None, OPTIONAL]: -- arg_string, = arg_strings -- value = self._get_value(action, arg_string) -- self._check_value(action, value) -- -- # PARSER arguments convert all values, but check only the first -- elif action.nargs is PARSER: -- value = [self._get_value(action, v) for v in arg_strings] -- self._check_value(action, value[0]) -- -- # all other types of nargs produce a list -- else: -- value = [self._get_value(action, v) for v in arg_strings] -- for v in value: -- self._check_value(action, v) -- -- # return the converted value -- return value -- -- def _get_value(self, action, arg_string): -- type_func = self._registry_get('type', action.type, action.type) -- if not hasattr(type_func, '__call__'): -- msg = _('%r is not callable') -- raise ArgumentError(action, msg % type_func) -- -- # convert the value to the appropriate type -- try: -- result = type_func(arg_string) -- -- # TypeErrors or ValueErrors indicate errors -- except (TypeError, ValueError): -- name = getattr(action.type, '__name__', repr(action.type)) -- msg = _('invalid %s value: %r') -- raise ArgumentError(action, msg % (name, arg_string)) -- -- # return the converted value -- return result -- -- def _check_value(self, action, value): -- # converted value must be one of the choices (if specified) -- if action.choices is not None and value not in action.choices: -- tup = value, ', '.join(map(repr, action.choices)) -- msg = _('invalid choice: %r (choose from %s)') % tup -- raise ArgumentError(action, msg) -- -- # ======================= -- # Help-formatting methods -- # ======================= -- def format_usage(self): -- formatter = self._get_formatter() -- formatter.add_usage(self.usage, self._actions, -- self._mutually_exclusive_groups) -- return formatter.format_help() -- -- def format_help(self): -- formatter = self._get_formatter() -- -- # usage -- formatter.add_usage(self.usage, self._actions, -- self._mutually_exclusive_groups) -- -- # description -- formatter.add_text(self.description) -- -- # positionals, optionals and user-defined groups -- for action_group in self._action_groups: -- formatter.start_section(action_group.title) -- formatter.add_text(action_group.description) -- formatter.add_arguments(action_group._group_actions) -- formatter.end_section() -- -- # epilog -- formatter.add_text(self.epilog) -- -- # determine help from format above -- return formatter.format_help() -- -- def format_version(self): -- formatter = self._get_formatter() -- formatter.add_text(self.version) -- return formatter.format_help() -- -- def _get_formatter(self): -- return self.formatter_class(prog=self.prog) -- -- # ===================== -- # Help-printing methods -- # ===================== -- def print_usage(self, file=None): -- self._print_message(self.format_usage(), file) -- -- def print_help(self, file=None): -- self._print_message(self.format_help(), file) -- -- def print_version(self, file=None): -- self._print_message(self.format_version(), file) -- -- def _print_message(self, message, file=None): -- if message: -- if file is None: -- file = _sys.stderr -- file.write(message) -- -- # =============== -- # Exiting methods -- # =============== -- def exit(self, status=0, message=None): -- if message: -- _sys.stderr.write(message) -- _sys.exit(status) -- -- def error(self, message): -- """error(message: string) -- -- Prints a usage message incorporating the message to stderr and -- exits. -- -- 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.prog, message)) -Index: ipython-0.10/IPython/external/configobj/_configobj.py -=================================================================== ---- /dev/null -+++ ipython-0.10/IPython/external/configobj/_configobj.py -@@ -0,0 +1,2501 @@ -+# configobj.py -+# A config file reader/writer that supports nested sections in config files. -+# Copyright (C) 2005-2008 Michael Foord, Nicola Larosa -+# E-mail: fuzzyman AT voidspace DOT org DOT uk -+# nico AT tekNico DOT net -+ -+# ConfigObj 4 -+# http://www.voidspace.org.uk/python/configobj.html -+ -+# Released subject to the BSD License -+# Please see http://www.voidspace.org.uk/python/license.shtml -+ -+# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml -+# For information about bugfixes, updates and support, please join the -+# ConfigObj mailing list: -+# http://lists.sourceforge.net/lists/listinfo/configobj-develop -+# Comments, suggestions and bug reports welcome. -+ -+from __future__ import generators -+ -+import sys -+INTP_VER = sys.version_info[:2] -+if INTP_VER < (2, 2): -+ raise RuntimeError("Python v.2.2 or later needed") -+ -+import os, re -+compiler = None -+try: -+ import compiler -+except ImportError: -+ # for IronPython -+ pass -+from types import StringTypes -+from warnings import warn -+try: -+ from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE -+except ImportError: -+ # Python 2.2 does not have these -+ # UTF-8 -+ BOM_UTF8 = '\xef\xbb\xbf' -+ # UTF-16, little endian -+ BOM_UTF16_LE = '\xff\xfe' -+ # UTF-16, big endian -+ BOM_UTF16_BE = '\xfe\xff' -+ if sys.byteorder == 'little': -+ # UTF-16, native endianness -+ BOM_UTF16 = BOM_UTF16_LE -+ else: -+ # UTF-16, native endianness -+ BOM_UTF16 = BOM_UTF16_BE -+ -+# A dictionary mapping BOM to -+# the encoding to decode with, and what to set the -+# encoding attribute to. -+BOMS = { -+ BOM_UTF8: ('utf_8', None), -+ BOM_UTF16_BE: ('utf16_be', 'utf_16'), -+ BOM_UTF16_LE: ('utf16_le', 'utf_16'), -+ BOM_UTF16: ('utf_16', 'utf_16'), -+ } -+# All legal variants of the BOM codecs. -+# TODO: the list of aliases is not meant to be exhaustive, is there a -+# better way ? -+BOM_LIST = { -+ 'utf_16': 'utf_16', -+ 'u16': 'utf_16', -+ 'utf16': 'utf_16', -+ 'utf-16': 'utf_16', -+ 'utf16_be': 'utf16_be', -+ 'utf_16_be': 'utf16_be', -+ 'utf-16be': 'utf16_be', -+ 'utf16_le': 'utf16_le', -+ 'utf_16_le': 'utf16_le', -+ 'utf-16le': 'utf16_le', -+ 'utf_8': 'utf_8', -+ 'u8': 'utf_8', -+ 'utf': 'utf_8', -+ 'utf8': 'utf_8', -+ 'utf-8': 'utf_8', -+ } -+ -+# Map of encodings to the BOM to write. -+BOM_SET = { -+ 'utf_8': BOM_UTF8, -+ 'utf_16': BOM_UTF16, -+ 'utf16_be': BOM_UTF16_BE, -+ 'utf16_le': BOM_UTF16_LE, -+ None: BOM_UTF8 -+ } -+ -+ -+def match_utf8(encoding): -+ return BOM_LIST.get(encoding.lower()) == 'utf_8' -+ -+ -+# Quote strings used for writing values -+squot = "'%s'" -+dquot = '"%s"' -+noquot = "%s" -+wspace_plus = ' \r\t\n\v\t\'"' -+tsquot = '"""%s"""' -+tdquot = "'''%s'''" -+ -+try: -+ enumerate -+except NameError: -+ def enumerate(obj): -+ """enumerate for Python 2.2.""" -+ i = -1 -+ for item in obj: -+ i += 1 -+ yield i, item -+ -+try: -+ True, False -+except NameError: -+ True, False = 1, 0 -+ -+ -+__version__ = '4.5.2' -+ -+__revision__ = '$Id: configobj.py 156 2006-01-31 14:57:08Z fuzzyman $' -+ -+__docformat__ = "restructuredtext en" -+ -+__all__ = ( -+ '__version__', -+ 'DEFAULT_INDENT_TYPE', -+ 'DEFAULT_INTERPOLATION', -+ 'ConfigObjError', -+ 'NestingError', -+ 'ParseError', -+ 'DuplicateError', -+ 'ConfigspecError', -+ 'ConfigObj', -+ 'SimpleVal', -+ 'InterpolationError', -+ 'InterpolationLoopError', -+ 'MissingInterpolationOption', -+ 'RepeatSectionError', -+ 'ReloadError', -+ 'UnreprError', -+ 'UnknownType', -+ '__docformat__', -+ 'flatten_errors', -+) -+ -+DEFAULT_INTERPOLATION = 'configparser' -+DEFAULT_INDENT_TYPE = ' ' -+MAX_INTERPOL_DEPTH = 10 -+ -+OPTION_DEFAULTS = { -+ 'interpolation': True, -+ 'raise_errors': False, -+ 'list_values': True, -+ 'create_empty': False, -+ 'file_error': False, -+ 'configspec': None, -+ 'stringify': True, -+ # option may be set to one of ('', ' ', '\t') -+ 'indent_type': None, -+ 'encoding': None, -+ 'default_encoding': None, -+ 'unrepr': False, -+ 'write_empty_values': False, -+} -+ -+ -+ -+def getObj(s): -+ s = "a=" + s -+ if compiler is None: -+ raise ImportError('compiler module not available') -+ p = compiler.parse(s) -+ return p.getChildren()[1].getChildren()[0].getChildren()[1] -+ -+ -+class UnknownType(Exception): -+ pass -+ -+ -+class Builder(object): -+ -+ def build(self, o): -+ m = getattr(self, 'build_' + o.__class__.__name__, None) -+ if m is None: -+ raise UnknownType(o.__class__.__name__) -+ return m(o) -+ -+ def build_List(self, o): -+ return map(self.build, o.getChildren()) -+ -+ def build_Const(self, o): -+ return o.value -+ -+ def build_Dict(self, o): -+ d = {} -+ i = iter(map(self.build, o.getChildren())) -+ for el in i: -+ d[el] = i.next() -+ return d -+ -+ def build_Tuple(self, o): -+ return tuple(self.build_List(o)) -+ -+ def build_Name(self, o): -+ if o.name == 'None': -+ return None -+ if o.name == 'True': -+ return True -+ if o.name == 'False': -+ return False -+ -+ # An undefined Name -+ raise UnknownType('Undefined Name') -+ -+ def build_Add(self, o): -+ real, imag = map(self.build_Const, o.getChildren()) -+ try: -+ real = float(real) -+ except TypeError: -+ raise UnknownType('Add') -+ if not isinstance(imag, complex) or imag.real != 0.0: -+ raise UnknownType('Add') -+ return real+imag -+ -+ def build_Getattr(self, o): -+ parent = self.build(o.expr) -+ return getattr(parent, o.attrname) -+ -+ def build_UnarySub(self, o): -+ return -self.build_Const(o.getChildren()[0]) -+ -+ def build_UnaryAdd(self, o): -+ return self.build_Const(o.getChildren()[0]) -+ -+ -+_builder = Builder() -+ -+ -+def unrepr(s): -+ if not s: -+ return s -+ return _builder.build(getObj(s)) -+ -+ -+ -+class ConfigObjError(SyntaxError): -+ """ -+ This is the base class for all errors that ConfigObj raises. -+ It is a subclass of SyntaxError. -+ """ -+ def __init__(self, message='', line_number=None, line=''): -+ self.line = line -+ self.line_number = line_number -+ self.message = message -+ SyntaxError.__init__(self, message) -+ -+ -+class NestingError(ConfigObjError): -+ """ -+ This error indicates a level of nesting that doesn't match. -+ """ -+ -+ -+class ParseError(ConfigObjError): -+ """ -+ This error indicates that a line is badly written. -+ It is neither a valid ``key = value`` line, -+ nor a valid section marker line. -+ """ -+ -+ -+class ReloadError(IOError): -+ """ -+ A 'reload' operation failed. -+ This exception is a subclass of ``IOError``. -+ """ -+ def __init__(self): -+ IOError.__init__(self, 'reload failed, filename is not set.') -+ -+ -+class DuplicateError(ConfigObjError): -+ """ -+ The keyword or section specified already exists. -+ """ -+ -+ -+class ConfigspecError(ConfigObjError): -+ """ -+ An error occured whilst parsing a configspec. -+ """ -+ -+ -+class InterpolationError(ConfigObjError): -+ """Base class for the two interpolation errors.""" -+ -+ -+class InterpolationLoopError(InterpolationError): -+ """Maximum interpolation depth exceeded in string interpolation.""" -+ -+ def __init__(self, option): -+ InterpolationError.__init__( -+ self, -+ 'interpolation loop detected in value "%s".' % option) -+ -+ -+class RepeatSectionError(ConfigObjError): -+ """ -+ This error indicates additional sections in a section with a -+ ``__many__`` (repeated) section. -+ """ -+ -+ -+class MissingInterpolationOption(InterpolationError): -+ """A value specified for interpolation was missing.""" -+ -+ def __init__(self, option): -+ InterpolationError.__init__( -+ self, -+ 'missing option "%s" in interpolation.' % option) -+ -+ -+class UnreprError(ConfigObjError): -+ """An error parsing in unrepr mode.""" -+ -+ -+ -+class InterpolationEngine(object): -+ """ -+ A helper class to help perform string interpolation. -+ -+ This class is an abstract base class; its descendants perform -+ the actual work. -+ """ -+ -+ # compiled regexp to use in self.interpolate() -+ _KEYCRE = re.compile(r"%\(([^)]*)\)s") -+ -+ def __init__(self, section): -+ # the Section instance that "owns" this engine -+ self.section = section -+ -+ -+ def interpolate(self, key, value): -+ def recursive_interpolate(key, value, section, backtrail): -+ """The function that does the actual work. -+ -+ ``value``: the string we're trying to interpolate. -+ ``section``: the section in which that string was found -+ ``backtrail``: a dict to keep track of where we've been, -+ to detect and prevent infinite recursion loops -+ -+ This is similar to a depth-first-search algorithm. -+ """ -+ # Have we been here already? -+ if backtrail.has_key((key, section.name)): -+ # Yes - infinite loop detected -+ raise InterpolationLoopError(key) -+ # Place a marker on our backtrail so we won't come back here again -+ backtrail[(key, section.name)] = 1 -+ -+ # Now start the actual work -+ match = self._KEYCRE.search(value) -+ while match: -+ # The actual parsing of the match is implementation-dependent, -+ # so delegate to our helper function -+ k, v, s = self._parse_match(match) -+ if k is None: -+ # That's the signal that no further interpolation is needed -+ replacement = v -+ else: -+ # Further interpolation may be needed to obtain final value -+ replacement = recursive_interpolate(k, v, s, backtrail) -+ # Replace the matched string with its final value -+ start, end = match.span() -+ value = ''.join((value[:start], replacement, value[end:])) -+ new_search_start = start + len(replacement) -+ # Pick up the next interpolation key, if any, for next time -+ # through the while loop -+ match = self._KEYCRE.search(value, new_search_start) -+ -+ # Now safe to come back here again; remove marker from backtrail -+ del backtrail[(key, section.name)] -+ -+ return value -+ -+ # Back in interpolate(), all we have to do is kick off the recursive -+ # function with appropriate starting values -+ value = recursive_interpolate(key, value, self.section, {}) -+ return value -+ -+ -+ def _fetch(self, key): -+ """Helper function to fetch values from owning section. -+ -+ Returns a 2-tuple: the value, and the section where it was found. -+ """ -+ # switch off interpolation before we try and fetch anything ! -+ save_interp = self.section.main.interpolation -+ self.section.main.interpolation = False -+ -+ # Start at section that "owns" this InterpolationEngine -+ current_section = self.section -+ while True: -+ # try the current section first -+ val = current_section.get(key) -+ if val is not None: -+ break -+ # try "DEFAULT" next -+ val = current_section.get('DEFAULT', {}).get(key) -+ if val is not None: -+ break -+ # move up to parent and try again -+ # top-level's parent is itself -+ if current_section.parent is current_section: -+ # reached top level, time to give up -+ break -+ current_section = current_section.parent -+ -+ # restore interpolation to previous value before returning -+ self.section.main.interpolation = save_interp -+ if val is None: -+ raise MissingInterpolationOption(key) -+ return val, current_section -+ -+ -+ def _parse_match(self, match): -+ """Implementation-dependent helper function. -+ -+ Will be passed a match object corresponding to the interpolation -+ key we just found (e.g., "%(foo)s" or "$foo"). Should look up that -+ key in the appropriate config file section (using the ``_fetch()`` -+ helper function) and return a 3-tuple: (key, value, section) -+ -+ ``key`` is the name of the key we're looking for -+ ``value`` is the value found for that key -+ ``section`` is a reference to the section where it was found -+ -+ ``key`` and ``section`` should be None if no further -+ interpolation should be performed on the resulting value -+ (e.g., if we interpolated "$$" and returned "$"). -+ """ -+ raise NotImplementedError() -+ -+ -+ -+class ConfigParserInterpolation(InterpolationEngine): -+ """Behaves like ConfigParser.""" -+ _KEYCRE = re.compile(r"%\(([^)]*)\)s") -+ -+ def _parse_match(self, match): -+ key = match.group(1) -+ value, section = self._fetch(key) -+ return key, value, section -+ -+ -+ -+class TemplateInterpolation(InterpolationEngine): -+ """Behaves like string.Template.""" -+ _delimiter = '$' -+ _KEYCRE = re.compile(r""" -+ \$(?: -+ (?P\$) | # Two $ signs -+ (?P[_a-z][_a-z0-9]*) | # $name format -+ {(?P[^}]*)} # ${name} format -+ ) -+ """, re.IGNORECASE | re.VERBOSE) -+ -+ def _parse_match(self, match): -+ # Valid name (in or out of braces): fetch value from section -+ key = match.group('named') or match.group('braced') -+ if key is not None: -+ value, section = self._fetch(key) -+ return key, value, section -+ # Escaped delimiter (e.g., $$): return single delimiter -+ if match.group('escaped') is not None: -+ # Return None for key and section to indicate it's time to stop -+ return None, self._delimiter, None -+ # Anything else: ignore completely, just return it unchanged -+ return None, match.group(), None -+ -+ -+interpolation_engines = { -+ 'configparser': ConfigParserInterpolation, -+ 'template': TemplateInterpolation, -+} -+ -+ -+ -+class Section(dict): -+ """ -+ A dictionary-like object that represents a section in a config file. -+ -+ It does string interpolation if the 'interpolation' attribute -+ of the 'main' object is set to True. -+ -+ Interpolation is tried first from this object, then from the 'DEFAULT' -+ section of this object, next from the parent and its 'DEFAULT' section, -+ and so on until the main object is reached. -+ -+ A Section will behave like an ordered dictionary - following the -+ order of the ``scalars`` and ``sections`` attributes. -+ You can use this to change the order of members. -+ -+ Iteration follows the order: scalars, then sections. -+ """ -+ -+ def __init__(self, parent, depth, main, indict=None, name=None): -+ """ -+ * parent is the section above -+ * depth is the depth level of this section -+ * main is the main ConfigObj -+ * indict is a dictionary to initialise the section with -+ """ -+ if indict is None: -+ indict = {} -+ dict.__init__(self) -+ # used for nesting level *and* interpolation -+ self.parent = parent -+ # used for the interpolation attribute -+ self.main = main -+ # level of nesting depth of this Section -+ self.depth = depth -+ # purely for information -+ self.name = name -+ # -+ self._initialise() -+ # we do this explicitly so that __setitem__ is used properly -+ # (rather than just passing to ``dict.__init__``) -+ for entry, value in indict.iteritems(): -+ self[entry] = value -+ -+ -+ def _initialise(self): -+ # the sequence of scalar values in this Section -+ self.scalars = [] -+ # the sequence of sections in this Section -+ self.sections = [] -+ # for comments :-) -+ self.comments = {} -+ self.inline_comments = {} -+ # for the configspec -+ self.configspec = {} -+ self._order = [] -+ self._configspec_comments = {} -+ self._configspec_inline_comments = {} -+ self._cs_section_comments = {} -+ self._cs_section_inline_comments = {} -+ # for defaults -+ self.defaults = [] -+ self.default_values = {} -+ -+ -+ def _interpolate(self, key, value): -+ try: -+ # do we already have an interpolation engine? -+ engine = self._interpolation_engine -+ except AttributeError: -+ # not yet: first time running _interpolate(), so pick the engine -+ name = self.main.interpolation -+ if name == True: # note that "if name:" would be incorrect here -+ # backwards-compatibility: interpolation=True means use default -+ name = DEFAULT_INTERPOLATION -+ name = name.lower() # so that "Template", "template", etc. all work -+ class_ = interpolation_engines.get(name, None) -+ if class_ is None: -+ # invalid value for self.main.interpolation -+ self.main.interpolation = False -+ return value -+ else: -+ # save reference to engine so we don't have to do this again -+ engine = self._interpolation_engine = class_(self) -+ # let the engine do the actual work -+ return engine.interpolate(key, value) -+ -+ -+ def __getitem__(self, key): -+ """Fetch the item and do string interpolation.""" -+ val = dict.__getitem__(self, key) -+ if self.main.interpolation and isinstance(val, StringTypes): -+ return self._interpolate(key, val) -+ return val -+ -+ -+ def __setitem__(self, key, value, unrepr=False): -+ """ -+ Correctly set a value. -+ -+ Making dictionary values Section instances. -+ (We have to special case 'Section' instances - which are also dicts) -+ -+ Keys must be strings. -+ Values need only be strings (or lists of strings) if -+ ``main.stringify`` is set. -+ -+ `unrepr`` must be set when setting a value to a dictionary, without -+ creating a new sub-section. -+ """ -+ if not isinstance(key, StringTypes): -+ raise ValueError('The key "%s" is not a string.' % key) -+ -+ # add the comment -+ if not self.comments.has_key(key): -+ self.comments[key] = [] -+ self.inline_comments[key] = '' -+ # remove the entry from defaults -+ if key in self.defaults: -+ self.defaults.remove(key) -+ # -+ if isinstance(value, Section): -+ if not self.has_key(key): -+ self.sections.append(key) -+ dict.__setitem__(self, key, value) -+ elif isinstance(value, dict) and not unrepr: -+ # First create the new depth level, -+ # then create the section -+ if not self.has_key(key): -+ self.sections.append(key) -+ new_depth = self.depth + 1 -+ dict.__setitem__( -+ self, -+ key, -+ Section( -+ self, -+ new_depth, -+ self.main, -+ indict=value, -+ name=key)) -+ else: -+ if not self.has_key(key): -+ self.scalars.append(key) -+ if not self.main.stringify: -+ if isinstance(value, StringTypes): -+ pass -+ elif isinstance(value, (list, tuple)): -+ for entry in value: -+ if not isinstance(entry, StringTypes): -+ raise TypeError('Value is not a string "%s".' % entry) -+ else: -+ raise TypeError('Value is not a string "%s".' % value) -+ dict.__setitem__(self, key, value) -+ -+ -+ def __delitem__(self, key): -+ """Remove items from the sequence when deleting.""" -+ dict. __delitem__(self, key) -+ if key in self.scalars: -+ self.scalars.remove(key) -+ else: -+ self.sections.remove(key) -+ del self.comments[key] -+ del self.inline_comments[key] -+ -+ -+ def get(self, key, default=None): -+ """A version of ``get`` that doesn't bypass string interpolation.""" -+ try: -+ return self[key] -+ except KeyError: -+ return default -+ -+ -+ def update(self, indict): -+ """ -+ A version of update that uses our ``__setitem__``. -+ """ -+ for entry in indict: -+ self[entry] = indict[entry] -+ -+ -+ def pop(self, key, *args): -+ """ -+ 'D.pop(k[,d]) -> v, remove specified key and return the corresponding value. -+ If key is not found, d is returned if given, otherwise KeyError is raised' -+ """ -+ val = dict.pop(self, key, *args) -+ if key in self.scalars: -+ del self.comments[key] -+ del self.inline_comments[key] -+ self.scalars.remove(key) -+ elif key in self.sections: -+ del self.comments[key] -+ del self.inline_comments[key] -+ self.sections.remove(key) -+ if self.main.interpolation and isinstance(val, StringTypes): -+ return self._interpolate(key, val) -+ return val -+ -+ -+ def popitem(self): -+ """Pops the first (key,val)""" -+ sequence = (self.scalars + self.sections) -+ if not sequence: -+ raise KeyError(": 'popitem(): dictionary is empty'") -+ key = sequence[0] -+ val = self[key] -+ del self[key] -+ return key, val -+ -+ -+ def clear(self): -+ """ -+ A version of clear that also affects scalars/sections -+ Also clears comments and configspec. -+ -+ Leaves other attributes alone : -+ depth/main/parent are not affected -+ """ -+ dict.clear(self) -+ self.scalars = [] -+ self.sections = [] -+ self.comments = {} -+ self.inline_comments = {} -+ self.configspec = {} -+ -+ -+ def setdefault(self, key, default=None): -+ """A version of setdefault that sets sequence if appropriate.""" -+ try: -+ return self[key] -+ except KeyError: -+ self[key] = default -+ return self[key] -+ -+ -+ def items(self): -+ """D.items() -> list of D's (key, value) pairs, as 2-tuples""" -+ return zip((self.scalars + self.sections), self.values()) -+ -+ -+ def keys(self): -+ """D.keys() -> list of D's keys""" -+ return (self.scalars + self.sections) -+ -+ -+ def values(self): -+ """D.values() -> list of D's values""" -+ return [self[key] for key in (self.scalars + self.sections)] -+ -+ -+ def iteritems(self): -+ """D.iteritems() -> an iterator over the (key, value) items of D""" -+ return iter(self.items()) -+ -+ -+ def iterkeys(self): -+ """D.iterkeys() -> an iterator over the keys of D""" -+ return iter((self.scalars + self.sections)) -+ -+ __iter__ = iterkeys -+ -+ -+ def itervalues(self): -+ """D.itervalues() -> an iterator over the values of D""" -+ return iter(self.values()) -+ -+ -+ def __repr__(self): -+ """x.__repr__() <==> repr(x)""" -+ return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(self[key]))) -+ for key in (self.scalars + self.sections)]) -+ -+ __str__ = __repr__ -+ __str__.__doc__ = "x.__str__() <==> str(x)" -+ -+ -+ # Extra methods - not in a normal dictionary -+ -+ def dict(self): -+ """ -+ Return a deepcopy of self as a dictionary. -+ -+ All members that are ``Section`` instances are recursively turned to -+ ordinary dictionaries - by calling their ``dict`` method. -+ -+ >>> n = a.dict() -+ >>> n == a -+ 1 -+ >>> n is a -+ 0 -+ """ -+ newdict = {} -+ for entry in self: -+ this_entry = self[entry] -+ if isinstance(this_entry, Section): -+ this_entry = this_entry.dict() -+ elif isinstance(this_entry, list): -+ # create a copy rather than a reference -+ this_entry = list(this_entry) -+ elif isinstance(this_entry, tuple): -+ # create a copy rather than a reference -+ this_entry = tuple(this_entry) -+ newdict[entry] = this_entry -+ return newdict -+ -+ -+ def merge(self, indict): -+ """ -+ A recursive update - useful for merging config files. -+ -+ >>> a = '''[section1] -+ ... option1 = True -+ ... [[subsection]] -+ ... more_options = False -+ ... # end of file'''.splitlines() -+ >>> b = '''# File is user.ini -+ ... [section1] -+ ... option1 = False -+ ... # end of file'''.splitlines() -+ >>> c1 = ConfigObj(b) -+ >>> c2 = ConfigObj(a) -+ >>> c2.merge(c1) -+ >>> c2 -+ {'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}} -+ """ -+ for key, val in indict.items(): -+ if (key in self and isinstance(self[key], dict) and -+ isinstance(val, dict)): -+ self[key].merge(val) -+ else: -+ self[key] = val -+ -+ -+ def rename(self, oldkey, newkey): -+ """ -+ Change a keyname to another, without changing position in sequence. -+ -+ Implemented so that transformations can be made on keys, -+ as well as on values. (used by encode and decode) -+ -+ Also renames comments. -+ """ -+ if oldkey in self.scalars: -+ the_list = self.scalars -+ elif oldkey in self.sections: -+ the_list = self.sections -+ else: -+ raise KeyError('Key "%s" not found.' % oldkey) -+ pos = the_list.index(oldkey) -+ # -+ val = self[oldkey] -+ dict.__delitem__(self, oldkey) -+ dict.__setitem__(self, newkey, val) -+ the_list.remove(oldkey) -+ the_list.insert(pos, newkey) -+ comm = self.comments[oldkey] -+ inline_comment = self.inline_comments[oldkey] -+ del self.comments[oldkey] -+ del self.inline_comments[oldkey] -+ self.comments[newkey] = comm -+ self.inline_comments[newkey] = inline_comment -+ -+ -+ def walk(self, function, raise_errors=True, -+ call_on_sections=False, **keywargs): -+ """ -+ Walk every member and call a function on the keyword and value. -+ -+ Return a dictionary of the return values -+ -+ If the function raises an exception, raise the errror -+ unless ``raise_errors=False``, in which case set the return value to -+ ``False``. -+ -+ Any unrecognised keyword arguments you pass to walk, will be pased on -+ to the function you pass in. -+ -+ Note: if ``call_on_sections`` is ``True`` then - on encountering a -+ subsection, *first* the function is called for the *whole* subsection, -+ and then recurses into it's members. This means your function must be -+ able to handle strings, dictionaries and lists. This allows you -+ to change the key of subsections as well as for ordinary members. The -+ return value when called on the whole subsection has to be discarded. -+ -+ See the encode and decode methods for examples, including functions. -+ -+ .. caution:: -+ -+ You can use ``walk`` to transform the names of members of a section -+ but you mustn't add or delete members. -+ -+ >>> config = '''[XXXXsection] -+ ... XXXXkey = XXXXvalue'''.splitlines() -+ >>> cfg = ConfigObj(config) -+ >>> cfg -+ {'XXXXsection': {'XXXXkey': 'XXXXvalue'}} -+ >>> def transform(section, key): -+ ... val = section[key] -+ ... newkey = key.replace('XXXX', 'CLIENT1') -+ ... section.rename(key, newkey) -+ ... if isinstance(val, (tuple, list, dict)): -+ ... pass -+ ... else: -+ ... val = val.replace('XXXX', 'CLIENT1') -+ ... section[newkey] = val -+ >>> cfg.walk(transform, call_on_sections=True) -+ {'CLIENT1section': {'CLIENT1key': None}} -+ >>> cfg -+ {'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}} -+ """ -+ out = {} -+ # scalars first -+ for i in range(len(self.scalars)): -+ entry = self.scalars[i] -+ try: -+ val = function(self, entry, **keywargs) -+ # bound again in case name has changed -+ entry = self.scalars[i] -+ out[entry] = val -+ except Exception: -+ if raise_errors: -+ raise -+ else: -+ entry = self.scalars[i] -+ out[entry] = False -+ # then sections -+ for i in range(len(self.sections)): -+ entry = self.sections[i] -+ if call_on_sections: -+ try: -+ function(self, entry, **keywargs) -+ except Exception: -+ if raise_errors: -+ raise -+ else: -+ entry = self.sections[i] -+ out[entry] = False -+ # bound again in case name has changed -+ entry = self.sections[i] -+ # previous result is discarded -+ out[entry] = self[entry].walk( -+ function, -+ raise_errors=raise_errors, -+ call_on_sections=call_on_sections, -+ **keywargs) -+ return out -+ -+ -+ def decode(self, encoding): -+ """ -+ Decode all strings and values to unicode, using the specified encoding. -+ -+ Works with subsections and list values. -+ -+ Uses the ``walk`` method. -+ -+ Testing ``encode`` and ``decode``. -+ >>> m = ConfigObj(a) -+ >>> m.decode('ascii') -+ >>> def testuni(val): -+ ... for entry in val: -+ ... if not isinstance(entry, unicode): -+ ... print >> sys.stderr, type(entry) -+ ... raise AssertionError, 'decode failed.' -+ ... if isinstance(val[entry], dict): -+ ... testuni(val[entry]) -+ ... elif not isinstance(val[entry], unicode): -+ ... raise AssertionError, 'decode failed.' -+ >>> testuni(m) -+ >>> m.encode('ascii') -+ >>> a == m -+ 1 -+ """ -+ warn('use of ``decode`` is deprecated.', DeprecationWarning) -+ def decode(section, key, encoding=encoding, warn=True): -+ """ """ -+ val = section[key] -+ if isinstance(val, (list, tuple)): -+ newval = [] -+ for entry in val: -+ newval.append(entry.decode(encoding)) -+ elif isinstance(val, dict): -+ newval = val -+ else: -+ newval = val.decode(encoding) -+ newkey = key.decode(encoding) -+ section.rename(key, newkey) -+ section[newkey] = newval -+ # using ``call_on_sections`` allows us to modify section names -+ self.walk(decode, call_on_sections=True) -+ -+ -+ def encode(self, encoding): -+ """ -+ Encode all strings and values from unicode, -+ using the specified encoding. -+ -+ Works with subsections and list values. -+ Uses the ``walk`` method. -+ """ -+ warn('use of ``encode`` is deprecated.', DeprecationWarning) -+ def encode(section, key, encoding=encoding): -+ """ """ -+ val = section[key] -+ if isinstance(val, (list, tuple)): -+ newval = [] -+ for entry in val: -+ newval.append(entry.encode(encoding)) -+ elif isinstance(val, dict): -+ newval = val -+ else: -+ newval = val.encode(encoding) -+ newkey = key.encode(encoding) -+ section.rename(key, newkey) -+ section[newkey] = newval -+ self.walk(encode, call_on_sections=True) -+ -+ -+ def istrue(self, key): -+ """A deprecated version of ``as_bool``.""" -+ warn('use of ``istrue`` is deprecated. Use ``as_bool`` method ' -+ 'instead.', DeprecationWarning) -+ return self.as_bool(key) -+ -+ -+ def as_bool(self, key): -+ """ -+ Accepts a key as input. The corresponding value must be a string or -+ the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to -+ retain compatibility with Python 2.2. -+ -+ If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns -+ ``True``. -+ -+ If the string is one of ``False``, ``Off``, ``No``, or ``0`` it returns -+ ``False``. -+ -+ ``as_bool`` is not case sensitive. -+ -+ Any other input will raise a ``ValueError``. -+ -+ >>> a = ConfigObj() -+ >>> a['a'] = 'fish' -+ >>> a.as_bool('a') -+ Traceback (most recent call last): -+ ValueError: Value "fish" is neither True nor False -+ >>> a['b'] = 'True' -+ >>> a.as_bool('b') -+ 1 -+ >>> a['b'] = 'off' -+ >>> a.as_bool('b') -+ 0 -+ """ -+ val = self[key] -+ if val == True: -+ return True -+ elif val == False: -+ return False -+ else: -+ try: -+ if not isinstance(val, StringTypes): -+ # TODO: Why do we raise a KeyError here? -+ raise KeyError() -+ else: -+ return self.main._bools[val.lower()] -+ except KeyError: -+ raise ValueError('Value "%s" is neither True nor False' % val) -+ -+ -+ def as_int(self, key): -+ """ -+ A convenience method which coerces the specified value to an integer. -+ -+ If the value is an invalid literal for ``int``, a ``ValueError`` will -+ be raised. -+ -+ >>> a = ConfigObj() -+ >>> a['a'] = 'fish' -+ >>> a.as_int('a') -+ Traceback (most recent call last): -+ ValueError: invalid literal for int(): fish -+ >>> a['b'] = '1' -+ >>> a.as_int('b') -+ 1 -+ >>> a['b'] = '3.2' -+ >>> a.as_int('b') -+ Traceback (most recent call last): -+ ValueError: invalid literal for int(): 3.2 -+ """ -+ return int(self[key]) -+ -+ -+ def as_float(self, key): -+ """ -+ A convenience method which coerces the specified value to a float. -+ -+ If the value is an invalid literal for ``float``, a ``ValueError`` will -+ be raised. -+ -+ >>> a = ConfigObj() -+ >>> a['a'] = 'fish' -+ >>> a.as_float('a') -+ Traceback (most recent call last): -+ ValueError: invalid literal for float(): fish -+ >>> a['b'] = '1' -+ >>> a.as_float('b') -+ 1.0 -+ >>> a['b'] = '3.2' -+ >>> a.as_float('b') -+ 3.2000000000000002 -+ """ -+ return float(self[key]) -+ -+ -+ def restore_default(self, key): -+ """ -+ Restore (and return) default value for the specified key. -+ -+ This method will only work for a ConfigObj that was created -+ with a configspec and has been validated. -+ -+ If there is no default value for this key, ``KeyError`` is raised. -+ """ -+ default = self.default_values[key] -+ dict.__setitem__(self, key, default) -+ if key not in self.defaults: -+ self.defaults.append(key) -+ return default -+ -+ -+ def restore_defaults(self): -+ """ -+ Recursively restore default values to all members -+ that have them. -+ -+ This method will only work for a ConfigObj that was created -+ with a configspec and has been validated. -+ -+ It doesn't delete or modify entries without default values. -+ """ -+ for key in self.default_values: -+ self.restore_default(key) -+ -+ for section in self.sections: -+ self[section].restore_defaults() -+ -+ -+class ConfigObj(Section): -+ """An object to read, create, and write config files.""" -+ -+ _keyword = re.compile(r'''^ # line start -+ (\s*) # indentation -+ ( # keyword -+ (?:".*?")| # double quotes -+ (?:'.*?')| # single quotes -+ (?:[^'"=].*?) # no quotes -+ ) -+ \s*=\s* # divider -+ (.*) # value (including list values and comments) -+ $ # line end -+ ''', -+ re.VERBOSE) -+ -+ _sectionmarker = re.compile(r'''^ -+ (\s*) # 1: indentation -+ ((?:\[\s*)+) # 2: section marker open -+ ( # 3: section name open -+ (?:"\s*\S.*?\s*")| # at least one non-space with double quotes -+ (?:'\s*\S.*?\s*')| # at least one non-space with single quotes -+ (?:[^'"\s].*?) # at least one non-space unquoted -+ ) # section name close -+ ((?:\s*\])+) # 4: section marker close -+ \s*(\#.*)? # 5: optional comment -+ $''', -+ re.VERBOSE) -+ -+ # this regexp pulls list values out as a single string -+ # or single values and comments -+ # FIXME: this regex adds a '' to the end of comma terminated lists -+ # workaround in ``_handle_value`` -+ _valueexp = re.compile(r'''^ -+ (?: -+ (?: -+ ( -+ (?: -+ (?: -+ (?:".*?")| # double quotes -+ (?:'.*?')| # single quotes -+ (?:[^'",\#][^,\#]*?) # unquoted -+ ) -+ \s*,\s* # comma -+ )* # match all list items ending in a comma (if any) -+ ) -+ ( -+ (?:".*?")| # double quotes -+ (?:'.*?')| # single quotes -+ (?:[^'",\#\s][^,]*?)| # unquoted -+ (?:(? 1: -+ msg = "Parsing failed with several errors.\nFirst error %s" % info -+ error = ConfigObjError(msg) -+ else: -+ error = self._errors[0] -+ # set the errors attribute; it's a list of tuples: -+ # (error_type, message, line_number) -+ error.errors = self._errors -+ # set the config attribute -+ error.config = self -+ raise error -+ # delete private attributes -+ del self._errors -+ -+ if configspec is None: -+ self.configspec = None -+ else: -+ self._handle_configspec(configspec) -+ -+ -+ def _initialise(self, options=None): -+ if options is None: -+ options = OPTION_DEFAULTS -+ -+ # initialise a few variables -+ self.filename = None -+ self._errors = [] -+ self.raise_errors = options['raise_errors'] -+ self.interpolation = options['interpolation'] -+ self.list_values = options['list_values'] -+ self.create_empty = options['create_empty'] -+ self.file_error = options['file_error'] -+ self.stringify = options['stringify'] -+ self.indent_type = options['indent_type'] -+ self.encoding = options['encoding'] -+ self.default_encoding = options['default_encoding'] -+ self.BOM = False -+ self.newlines = None -+ self.write_empty_values = options['write_empty_values'] -+ self.unrepr = options['unrepr'] -+ -+ self.initial_comment = [] -+ self.final_comment = [] -+ self.configspec = {} -+ -+ # Clear section attributes as well -+ Section._initialise(self) -+ -+ -+ def __repr__(self): -+ return ('ConfigObj({%s})' % -+ ', '.join([('%s: %s' % (repr(key), repr(self[key]))) -+ for key in (self.scalars + self.sections)])) -+ -+ -+ def _handle_bom(self, infile): -+ """ -+ Handle any BOM, and decode if necessary. -+ -+ If an encoding is specified, that *must* be used - but the BOM should -+ still be removed (and the BOM attribute set). -+ -+ (If the encoding is wrongly specified, then a BOM for an alternative -+ encoding won't be discovered or removed.) -+ -+ If an encoding is not specified, UTF8 or UTF16 BOM will be detected and -+ removed. The BOM attribute will be set. UTF16 will be decoded to -+ unicode. -+ -+ NOTE: This method must not be called with an empty ``infile``. -+ -+ Specifying the *wrong* encoding is likely to cause a -+ ``UnicodeDecodeError``. -+ -+ ``infile`` must always be returned as a list of lines, but may be -+ passed in as a single string. -+ """ -+ if ((self.encoding is not None) and -+ (self.encoding.lower() not in BOM_LIST)): -+ # No need to check for a BOM -+ # the encoding specified doesn't have one -+ # just decode -+ return self._decode(infile, self.encoding) -+ -+ if isinstance(infile, (list, tuple)): -+ line = infile[0] -+ else: -+ line = infile -+ if self.encoding is not None: -+ # encoding explicitly supplied -+ # And it could have an associated BOM -+ # TODO: if encoding is just UTF16 - we ought to check for both -+ # TODO: big endian and little endian versions. -+ enc = BOM_LIST[self.encoding.lower()] -+ if enc == 'utf_16': -+ # For UTF16 we try big endian and little endian -+ for BOM, (encoding, final_encoding) in BOMS.items(): -+ if not final_encoding: -+ # skip UTF8 -+ continue -+ if infile.startswith(BOM): -+ ### BOM discovered -+ ##self.BOM = True -+ # Don't need to remove BOM -+ return self._decode(infile, encoding) -+ -+ # If we get this far, will *probably* raise a DecodeError -+ # As it doesn't appear to start with a BOM -+ return self._decode(infile, self.encoding) -+ -+ # Must be UTF8 -+ BOM = BOM_SET[enc] -+ if not line.startswith(BOM): -+ return self._decode(infile, self.encoding) -+ -+ newline = line[len(BOM):] -+ -+ # BOM removed -+ if isinstance(infile, (list, tuple)): -+ infile[0] = newline -+ else: -+ infile = newline -+ self.BOM = True -+ return self._decode(infile, self.encoding) -+ -+ # No encoding specified - so we need to check for UTF8/UTF16 -+ for BOM, (encoding, final_encoding) in BOMS.items(): -+ if not line.startswith(BOM): -+ continue -+ else: -+ # BOM discovered -+ self.encoding = final_encoding -+ if not final_encoding: -+ self.BOM = True -+ # UTF8 -+ # remove BOM -+ newline = line[len(BOM):] -+ if isinstance(infile, (list, tuple)): -+ infile[0] = newline -+ else: -+ infile = newline -+ # UTF8 - don't decode -+ if isinstance(infile, StringTypes): -+ return infile.splitlines(True) -+ else: -+ return infile -+ # UTF16 - have to decode -+ return self._decode(infile, encoding) -+ -+ # No BOM discovered and no encoding specified, just return -+ if isinstance(infile, StringTypes): -+ # infile read from a file will be a single string -+ return infile.splitlines(True) -+ return infile -+ -+ -+ def _a_to_u(self, aString): -+ """Decode ASCII strings to unicode if a self.encoding is specified.""" -+ if self.encoding: -+ return aString.decode('ascii') -+ else: -+ return aString -+ -+ -+ def _decode(self, infile, encoding): -+ """ -+ Decode infile to unicode. Using the specified encoding. -+ -+ if is a string, it also needs converting to a list. -+ """ -+ if isinstance(infile, StringTypes): -+ # can't be unicode -+ # NOTE: Could raise a ``UnicodeDecodeError`` -+ return infile.decode(encoding).splitlines(True) -+ for i, line in enumerate(infile): -+ if not isinstance(line, unicode): -+ # NOTE: The isinstance test here handles mixed lists of unicode/string -+ # NOTE: But the decode will break on any non-string values -+ # NOTE: Or could raise a ``UnicodeDecodeError`` -+ infile[i] = line.decode(encoding) -+ return infile -+ -+ -+ def _decode_element(self, line): -+ """Decode element to unicode if necessary.""" -+ if not self.encoding: -+ return line -+ if isinstance(line, str) and self.default_encoding: -+ return line.decode(self.default_encoding) -+ return line -+ -+ -+ def _str(self, value): -+ """ -+ Used by ``stringify`` within validate, to turn non-string values -+ into strings. -+ """ -+ if not isinstance(value, StringTypes): -+ return str(value) -+ else: -+ return value -+ -+ -+ def _parse(self, infile): -+ """Actually parse the config file.""" -+ temp_list_values = self.list_values -+ if self.unrepr: -+ self.list_values = False -+ -+ comment_list = [] -+ done_start = False -+ this_section = self -+ maxline = len(infile) - 1 -+ cur_index = -1 -+ reset_comment = False -+ -+ while cur_index < maxline: -+ if reset_comment: -+ comment_list = [] -+ cur_index += 1 -+ line = infile[cur_index] -+ sline = line.strip() -+ # do we have anything on the line ? -+ if not sline or sline.startswith('#'): -+ reset_comment = False -+ comment_list.append(line) -+ continue -+ -+ if not done_start: -+ # preserve initial comment -+ self.initial_comment = comment_list -+ comment_list = [] -+ done_start = True -+ -+ reset_comment = True -+ # first we check if it's a section marker -+ mat = self._sectionmarker.match(line) -+ if mat is not None: -+ # is a section line -+ (indent, sect_open, sect_name, sect_close, comment) = mat.groups() -+ if indent and (self.indent_type is None): -+ self.indent_type = indent -+ cur_depth = sect_open.count('[') -+ if cur_depth != sect_close.count(']'): -+ self._handle_error("Cannot compute the section depth at line %s.", -+ NestingError, infile, cur_index) -+ continue -+ -+ if cur_depth < this_section.depth: -+ # the new section is dropping back to a previous level -+ try: -+ parent = self._match_depth(this_section, -+ cur_depth).parent -+ except SyntaxError: -+ self._handle_error("Cannot compute nesting level at line %s.", -+ NestingError, infile, cur_index) -+ continue -+ elif cur_depth == this_section.depth: -+ # the new section is a sibling of the current section -+ parent = this_section.parent -+ elif cur_depth == this_section.depth + 1: -+ # the new section is a child the current section -+ parent = this_section -+ else: -+ self._handle_error("Section too nested at line %s.", -+ NestingError, infile, cur_index) -+ -+ sect_name = self._unquote(sect_name) -+ if parent.has_key(sect_name): -+ self._handle_error('Duplicate section name at line %s.', -+ DuplicateError, infile, cur_index) -+ continue -+ -+ # create the new section -+ this_section = Section( -+ parent, -+ cur_depth, -+ self, -+ name=sect_name) -+ parent[sect_name] = this_section -+ parent.inline_comments[sect_name] = comment -+ parent.comments[sect_name] = comment_list -+ continue -+ # -+ # it's not a section marker, -+ # so it should be a valid ``key = value`` line -+ mat = self._keyword.match(line) -+ if mat is None: -+ # it neither matched as a keyword -+ # or a section marker -+ self._handle_error( -+ 'Invalid line at line "%s".', -+ ParseError, infile, cur_index) -+ else: -+ # is a keyword value -+ # value will include any inline comment -+ (indent, key, value) = mat.groups() -+ if indent and (self.indent_type is None): -+ self.indent_type = indent -+ # check for a multiline value -+ if value[:3] in ['"""', "'''"]: -+ try: -+ (value, comment, cur_index) = self._multiline( -+ value, infile, cur_index, maxline) -+ except SyntaxError: -+ self._handle_error( -+ 'Parse error in value at line %s.', -+ ParseError, infile, cur_index) -+ continue -+ else: -+ if self.unrepr: -+ comment = '' -+ try: -+ value = unrepr(value) -+ except Exception, e: -+ if type(e) == UnknownType: -+ msg = 'Unknown name or type in value at line %s.' -+ else: -+ msg = 'Parse error in value at line %s.' -+ self._handle_error(msg, UnreprError, infile, -+ cur_index) -+ continue -+ else: -+ if self.unrepr: -+ comment = '' -+ try: -+ value = unrepr(value) -+ except Exception, e: -+ if isinstance(e, UnknownType): -+ msg = 'Unknown name or type in value at line %s.' -+ else: -+ msg = 'Parse error in value at line %s.' -+ self._handle_error(msg, UnreprError, infile, -+ cur_index) -+ continue -+ else: -+ # extract comment and lists -+ try: -+ (value, comment) = self._handle_value(value) -+ except SyntaxError: -+ self._handle_error( -+ 'Parse error in value at line %s.', -+ ParseError, infile, cur_index) -+ continue -+ # -+ key = self._unquote(key) -+ if this_section.has_key(key): -+ self._handle_error( -+ 'Duplicate keyword name at line %s.', -+ DuplicateError, infile, cur_index) -+ continue -+ # add the key. -+ # we set unrepr because if we have got this far we will never -+ # be creating a new section -+ this_section.__setitem__(key, value, unrepr=True) -+ this_section.inline_comments[key] = comment -+ this_section.comments[key] = comment_list -+ continue -+ # -+ if self.indent_type is None: -+ # no indentation used, set the type accordingly -+ self.indent_type = '' -+ -+ # preserve the final comment -+ if not self and not self.initial_comment: -+ self.initial_comment = comment_list -+ elif not reset_comment: -+ self.final_comment = comment_list -+ self.list_values = temp_list_values -+ -+ -+ def _match_depth(self, sect, depth): -+ """ -+ Given a section and a depth level, walk back through the sections -+ parents to see if the depth level matches a previous section. -+ -+ Return a reference to the right section, -+ or raise a SyntaxError. -+ """ -+ while depth < sect.depth: -+ if sect is sect.parent: -+ # we've reached the top level already -+ raise SyntaxError() -+ sect = sect.parent -+ if sect.depth == depth: -+ return sect -+ # shouldn't get here -+ raise SyntaxError() -+ -+ -+ def _handle_error(self, text, ErrorClass, infile, cur_index): -+ """ -+ Handle an error according to the error settings. -+ -+ Either raise the error or store it. -+ The error will have occured at ``cur_index`` -+ """ -+ line = infile[cur_index] -+ cur_index += 1 -+ message = text % cur_index -+ error = ErrorClass(message, cur_index, line) -+ if self.raise_errors: -+ # raise the error - parsing stops here -+ raise error -+ # store the error -+ # reraise when parsing has finished -+ self._errors.append(error) -+ -+ -+ def _unquote(self, value): -+ """Return an unquoted version of a value""" -+ if (value[0] == value[-1]) and (value[0] in ('"', "'")): -+ value = value[1:-1] -+ return value -+ -+ -+ def _quote(self, value, multiline=True): -+ """ -+ Return a safely quoted version of a value. -+ -+ Raise a ConfigObjError if the value cannot be safely quoted. -+ If multiline is ``True`` (default) then use triple quotes -+ if necessary. -+ -+ Don't quote values that don't need it. -+ Recursively quote members of a list and return a comma joined list. -+ Multiline is ``False`` for lists. -+ Obey list syntax for empty and single member lists. -+ -+ If ``list_values=False`` then the value is only quoted if it contains -+ a ``\n`` (is multiline) or '#'. -+ -+ If ``write_empty_values`` is set, and the value is an empty string, it -+ won't be quoted. -+ """ -+ if multiline and self.write_empty_values and value == '': -+ # Only if multiline is set, so that it is used for values not -+ # keys, and not values that are part of a list -+ return '' -+ -+ if multiline and isinstance(value, (list, tuple)): -+ if not value: -+ return ',' -+ elif len(value) == 1: -+ return self._quote(value[0], multiline=False) + ',' -+ return ', '.join([self._quote(val, multiline=False) -+ for val in value]) -+ if not isinstance(value, StringTypes): -+ if self.stringify: -+ value = str(value) -+ else: -+ raise TypeError('Value "%s" is not a string.' % value) -+ -+ if not value: -+ return '""' -+ -+ no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value -+ need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value )) -+ hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value) -+ check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote -+ -+ if check_for_single: -+ if not self.list_values: -+ # we don't quote if ``list_values=False`` -+ quot = noquot -+ # for normal values either single or double quotes will do -+ elif '\n' in value: -+ # will only happen if multiline is off - e.g. '\n' in key -+ raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) -+ elif ((value[0] not in wspace_plus) and -+ (value[-1] not in wspace_plus) and -+ (',' not in value)): -+ quot = noquot -+ else: -+ quot = self._get_single_quote(value) -+ else: -+ # if value has '\n' or "'" *and* '"', it will need triple quotes -+ quot = self._get_triple_quote(value) -+ -+ if quot == noquot and '#' in value and self.list_values: -+ quot = self._get_single_quote(value) -+ -+ return quot % value -+ -+ -+ def _get_single_quote(self, value): -+ if ("'" in value) and ('"' in value): -+ raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) -+ elif '"' in value: -+ quot = squot -+ else: -+ quot = dquot -+ return quot -+ -+ -+ def _get_triple_quote(self, value): -+ if (value.find('"""') != -1) and (value.find("'''") != -1): -+ raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) -+ if value.find('"""') == -1: -+ quot = tdquot -+ else: -+ quot = tsquot -+ return quot -+ -+ -+ def _handle_value(self, value): -+ """ -+ Given a value string, unquote, remove comment, -+ handle lists. (including empty and single member lists) -+ """ -+ # do we look for lists in values ? -+ if not self.list_values: -+ mat = self._nolistvalue.match(value) -+ if mat is None: -+ raise SyntaxError() -+ # NOTE: we don't unquote here -+ return mat.groups() -+ # -+ mat = self._valueexp.match(value) -+ if mat is None: -+ # the value is badly constructed, probably badly quoted, -+ # or an invalid list -+ raise SyntaxError() -+ (list_values, single, empty_list, comment) = mat.groups() -+ if (list_values == '') and (single is None): -+ # change this if you want to accept empty values -+ raise SyntaxError() -+ # NOTE: note there is no error handling from here if the regex -+ # is wrong: then incorrect values will slip through -+ if empty_list is not None: -+ # the single comma - meaning an empty list -+ return ([], comment) -+ if single is not None: -+ # handle empty values -+ if list_values and not single: -+ # FIXME: the '' is a workaround because our regex now matches -+ # '' at the end of a list if it has a trailing comma -+ single = None -+ else: -+ single = single or '""' -+ single = self._unquote(single) -+ if list_values == '': -+ # not a list value -+ return (single, comment) -+ the_list = self._listvalueexp.findall(list_values) -+ the_list = [self._unquote(val) for val in the_list] -+ if single is not None: -+ the_list += [single] -+ return (the_list, comment) -+ -+ -+ def _multiline(self, value, infile, cur_index, maxline): -+ """Extract the value, where we are in a multiline situation.""" -+ quot = value[:3] -+ newvalue = value[3:] -+ single_line = self._triple_quote[quot][0] -+ multi_line = self._triple_quote[quot][1] -+ mat = single_line.match(value) -+ if mat is not None: -+ retval = list(mat.groups()) -+ retval.append(cur_index) -+ return retval -+ elif newvalue.find(quot) != -1: -+ # somehow the triple quote is missing -+ raise SyntaxError() -+ # -+ while cur_index < maxline: -+ cur_index += 1 -+ newvalue += '\n' -+ line = infile[cur_index] -+ if line.find(quot) == -1: -+ newvalue += line -+ else: -+ # end of multiline, process it -+ break -+ else: -+ # we've got to the end of the config, oops... -+ raise SyntaxError() -+ mat = multi_line.match(line) -+ if mat is None: -+ # a badly formed line -+ raise SyntaxError() -+ (value, comment) = mat.groups() -+ return (newvalue + value, comment, cur_index) -+ -+ -+ def _handle_configspec(self, configspec): -+ """Parse the configspec.""" -+ # FIXME: Should we check that the configspec was created with the -+ # correct settings ? (i.e. ``list_values=False``) -+ if not isinstance(configspec, ConfigObj): -+ try: -+ configspec = ConfigObj(configspec, -+ raise_errors=True, -+ file_error=True, -+ list_values=False) -+ except ConfigObjError, e: -+ # FIXME: Should these errors have a reference -+ # to the already parsed ConfigObj ? -+ raise ConfigspecError('Parsing configspec failed: %s' % e) -+ except IOError, e: -+ raise IOError('Reading configspec failed: %s' % e) -+ -+ self._set_configspec_value(configspec, self) -+ -+ -+ def _set_configspec_value(self, configspec, section): -+ """Used to recursively set configspec values.""" -+ if '__many__' in configspec.sections: -+ section.configspec['__many__'] = configspec['__many__'] -+ if len(configspec.sections) > 1: -+ # FIXME: can we supply any useful information here ? -+ raise RepeatSectionError() -+ -+ if hasattr(configspec, 'initial_comment'): -+ section._configspec_initial_comment = configspec.initial_comment -+ section._configspec_final_comment = configspec.final_comment -+ section._configspec_encoding = configspec.encoding -+ section._configspec_BOM = configspec.BOM -+ section._configspec_newlines = configspec.newlines -+ section._configspec_indent_type = configspec.indent_type -+ -+ for entry in configspec.scalars: -+ section._configspec_comments[entry] = configspec.comments[entry] -+ section._configspec_inline_comments[entry] = configspec.inline_comments[entry] -+ section.configspec[entry] = configspec[entry] -+ section._order.append(entry) -+ -+ for entry in configspec.sections: -+ if entry == '__many__': -+ continue -+ -+ section._cs_section_comments[entry] = configspec.comments[entry] -+ section._cs_section_inline_comments[entry] = configspec.inline_comments[entry] -+ if not section.has_key(entry): -+ section[entry] = {} -+ self._set_configspec_value(configspec[entry], section[entry]) -+ -+ -+ def _handle_repeat(self, section, configspec): -+ """Dynamically assign configspec for repeated section.""" -+ try: -+ section_keys = configspec.sections -+ scalar_keys = configspec.scalars -+ except AttributeError: -+ section_keys = [entry for entry in configspec -+ if isinstance(configspec[entry], dict)] -+ scalar_keys = [entry for entry in configspec -+ if not isinstance(configspec[entry], dict)] -+ -+ if '__many__' in section_keys and len(section_keys) > 1: -+ # FIXME: can we supply any useful information here ? -+ raise RepeatSectionError() -+ -+ scalars = {} -+ sections = {} -+ for entry in scalar_keys: -+ val = configspec[entry] -+ scalars[entry] = val -+ for entry in section_keys: -+ val = configspec[entry] -+ if entry == '__many__': -+ scalars[entry] = val -+ continue -+ sections[entry] = val -+ -+ section.configspec = scalars -+ for entry in sections: -+ if not section.has_key(entry): -+ section[entry] = {} -+ self._handle_repeat(section[entry], sections[entry]) -+ -+ -+ def _write_line(self, indent_string, entry, this_entry, comment): -+ """Write an individual line, for the write method""" -+ # NOTE: the calls to self._quote here handles non-StringType values. -+ if not self.unrepr: -+ val = self._decode_element(self._quote(this_entry)) -+ else: -+ val = repr(this_entry) -+ return '%s%s%s%s%s' % (indent_string, -+ self._decode_element(self._quote(entry, multiline=False)), -+ self._a_to_u(' = '), -+ val, -+ self._decode_element(comment)) -+ -+ -+ def _write_marker(self, indent_string, depth, entry, comment): -+ """Write a section marker line""" -+ return '%s%s%s%s%s' % (indent_string, -+ self._a_to_u('[' * depth), -+ self._quote(self._decode_element(entry), multiline=False), -+ self._a_to_u(']' * depth), -+ self._decode_element(comment)) -+ -+ -+ def _handle_comment(self, comment): -+ """Deal with a comment.""" -+ if not comment: -+ return '' -+ start = self.indent_type -+ if not comment.startswith('#'): -+ start += self._a_to_u(' # ') -+ return (start + comment) -+ -+ -+ # Public methods -+ -+ def write(self, outfile=None, section=None): -+ """ -+ Write the current ConfigObj as a file -+ -+ tekNico: FIXME: use StringIO instead of real files -+ -+ >>> filename = a.filename -+ >>> a.filename = 'test.ini' -+ >>> a.write() -+ >>> a.filename = filename -+ >>> a == ConfigObj('test.ini', raise_errors=True) -+ 1 -+ """ -+ if self.indent_type is None: -+ # this can be true if initialised from a dictionary -+ self.indent_type = DEFAULT_INDENT_TYPE -+ -+ out = [] -+ cs = self._a_to_u('#') -+ csp = self._a_to_u('# ') -+ if section is None: -+ int_val = self.interpolation -+ self.interpolation = False -+ section = self -+ for line in self.initial_comment: -+ line = self._decode_element(line) -+ stripped_line = line.strip() -+ if stripped_line and not stripped_line.startswith(cs): -+ line = csp + line -+ out.append(line) -+ -+ indent_string = self.indent_type * section.depth -+ for entry in (section.scalars + section.sections): -+ if entry in section.defaults: -+ # don't write out default values -+ continue -+ for comment_line in section.comments[entry]: -+ comment_line = self._decode_element(comment_line.lstrip()) -+ if comment_line and not comment_line.startswith(cs): -+ comment_line = csp + comment_line -+ out.append(indent_string + comment_line) -+ this_entry = section[entry] -+ comment = self._handle_comment(section.inline_comments[entry]) -+ -+ if isinstance(this_entry, dict): -+ # a section -+ out.append(self._write_marker( -+ indent_string, -+ this_entry.depth, -+ entry, -+ comment)) -+ out.extend(self.write(section=this_entry)) -+ else: -+ out.append(self._write_line( -+ indent_string, -+ entry, -+ this_entry, -+ comment)) -+ -+ if section is self: -+ for line in self.final_comment: -+ line = self._decode_element(line) -+ stripped_line = line.strip() -+ if stripped_line and not stripped_line.startswith(cs): -+ line = csp + line -+ out.append(line) -+ self.interpolation = int_val -+ -+ if section is not self: -+ return out -+ -+ if (self.filename is None) and (outfile is None): -+ # output a list of lines -+ # might need to encode -+ # NOTE: This will *screw* UTF16, each line will start with the BOM -+ if self.encoding: -+ out = [l.encode(self.encoding) for l in out] -+ if (self.BOM and ((self.encoding is None) or -+ (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))): -+ # Add the UTF8 BOM -+ if not out: -+ out.append('') -+ out[0] = BOM_UTF8 + out[0] -+ return out -+ -+ # Turn the list to a string, joined with correct newlines -+ newline = self.newlines or os.linesep -+ output = self._a_to_u(newline).join(out) -+ if self.encoding: -+ output = output.encode(self.encoding) -+ if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)): -+ # Add the UTF8 BOM -+ output = BOM_UTF8 + output -+ -+ if not output.endswith(newline): -+ output += newline -+ if outfile is not None: -+ outfile.write(output) -+ else: -+ h = open(self.filename, 'wb') -+ h.write(output) -+ h.close() -+ -+ -+ def validate(self, validator, preserve_errors=False, copy=False, -+ section=None): -+ """ -+ Test the ConfigObj against a configspec. -+ -+ It uses the ``validator`` object from *validate.py*. -+ -+ To run ``validate`` on the current ConfigObj, call: :: -+ -+ test = config.validate(validator) -+ -+ (Normally having previously passed in the configspec when the ConfigObj -+ was created - you can dynamically assign a dictionary of checks to the -+ ``configspec`` attribute of a section though). -+ -+ It returns ``True`` if everything passes, or a dictionary of -+ pass/fails (True/False). If every member of a subsection passes, it -+ will just have the value ``True``. (It also returns ``False`` if all -+ members fail). -+ -+ In addition, it converts the values from strings to their native -+ types if their checks pass (and ``stringify`` is set). -+ -+ If ``preserve_errors`` is ``True`` (``False`` is default) then instead -+ of a marking a fail with a ``False``, it will preserve the actual -+ exception object. This can contain info about the reason for failure. -+ For example the ``VdtValueTooSmallError`` indicates that the value -+ supplied was too small. If a value (or section) is missing it will -+ still be marked as ``False``. -+ -+ You must have the validate module to use ``preserve_errors=True``. -+ -+ You can then use the ``flatten_errors`` function to turn your nested -+ results dictionary into a flattened list of failures - useful for -+ displaying meaningful error messages. -+ """ -+ if section is None: -+ if self.configspec is None: -+ raise ValueError('No configspec supplied.') -+ if preserve_errors: -+ # We do this once to remove a top level dependency on the validate module -+ # Which makes importing configobj faster -+ from validate import VdtMissingValue -+ self._vdtMissingValue = VdtMissingValue -+ section = self -+ # -+ spec_section = section.configspec -+ if copy and hasattr(section, '_configspec_initial_comment'): -+ section.initial_comment = section._configspec_initial_comment -+ section.final_comment = section._configspec_final_comment -+ section.encoding = section._configspec_encoding -+ section.BOM = section._configspec_BOM -+ section.newlines = section._configspec_newlines -+ section.indent_type = section._configspec_indent_type -+ -+ if '__many__' in section.configspec: -+ many = spec_section['__many__'] -+ # dynamically assign the configspecs -+ # for the sections below -+ for entry in section.sections: -+ self._handle_repeat(section[entry], many) -+ # -+ out = {} -+ ret_true = True -+ ret_false = True -+ order = [k for k in section._order if k in spec_section] -+ order += [k for k in spec_section if k not in order] -+ for entry in order: -+ if entry == '__many__': -+ continue -+ if (not entry in section.scalars) or (entry in section.defaults): -+ # missing entries -+ # or entries from defaults -+ missing = True -+ val = None -+ if copy and not entry in section.scalars: -+ # copy comments -+ section.comments[entry] = ( -+ section._configspec_comments.get(entry, [])) -+ section.inline_comments[entry] = ( -+ section._configspec_inline_comments.get(entry, '')) -+ # -+ else: -+ missing = False -+ val = section[entry] -+ try: -+ check = validator.check(spec_section[entry], -+ val, -+ missing=missing -+ ) -+ except validator.baseErrorClass, e: -+ if not preserve_errors or isinstance(e, self._vdtMissingValue): -+ out[entry] = False -+ else: -+ # preserve the error -+ out[entry] = e -+ ret_false = False -+ ret_true = False -+ else: -+ try: -+ section.default_values.pop(entry, None) -+ except AttributeError: -+ # For Python 2.2 compatibility -+ try: -+ del section.default_values[entry] -+ except KeyError: -+ pass -+ -+ if hasattr(validator, 'get_default_value'): -+ try: -+ section.default_values[entry] = validator.get_default_value(spec_section[entry]) -+ except KeyError: -+ # No default -+ pass -+ -+ ret_false = False -+ out[entry] = True -+ if self.stringify or missing: -+ # if we are doing type conversion -+ # or the value is a supplied default -+ if not self.stringify: -+ if isinstance(check, (list, tuple)): -+ # preserve lists -+ check = [self._str(item) for item in check] -+ elif missing and check is None: -+ # convert the None from a default to a '' -+ check = '' -+ else: -+ check = self._str(check) -+ if (check != val) or missing: -+ section[entry] = check -+ if not copy and missing and entry not in section.defaults: -+ section.defaults.append(entry) -+ # Missing sections will have been created as empty ones when the -+ # configspec was read. -+ for entry in section.sections: -+ # FIXME: this means DEFAULT is not copied in copy mode -+ if section is self and entry == 'DEFAULT': -+ continue -+ if copy: -+ section.comments[entry] = section._cs_section_comments[entry] -+ section.inline_comments[entry] = ( -+ section._cs_section_inline_comments[entry]) -+ check = self.validate(validator, preserve_errors=preserve_errors, -+ copy=copy, section=section[entry]) -+ out[entry] = check -+ if check == False: -+ ret_true = False -+ elif check == True: -+ ret_false = False -+ else: -+ ret_true = False -+ ret_false = False -+ # -+ if ret_true: -+ return True -+ elif ret_false: -+ return False -+ return out -+ -+ -+ def reset(self): -+ """Clear ConfigObj instance and restore to 'freshly created' state.""" -+ self.clear() -+ self._initialise() -+ # FIXME: Should be done by '_initialise', but ConfigObj constructor (and reload) -+ # requires an empty dictionary -+ self.configspec = None -+ # Just to be sure ;-) -+ self._original_configspec = None -+ -+ -+ def reload(self): -+ """ -+ Reload a ConfigObj from file. -+ -+ This method raises a ``ReloadError`` if the ConfigObj doesn't have -+ a filename attribute pointing to a file. -+ """ -+ if not isinstance(self.filename, StringTypes): -+ raise ReloadError() -+ -+ filename = self.filename -+ current_options = {} -+ for entry in OPTION_DEFAULTS: -+ if entry == 'configspec': -+ continue -+ current_options[entry] = getattr(self, entry) -+ -+ configspec = self._original_configspec -+ current_options['configspec'] = configspec -+ -+ self.clear() -+ self._initialise(current_options) -+ self._load(filename, configspec) -+ -+ -+ -+class SimpleVal(object): -+ """ -+ A simple validator. -+ Can be used to check that all members expected are present. -+ -+ To use it, provide a configspec with all your members in (the value given -+ will be ignored). Pass an instance of ``SimpleVal`` to the ``validate`` -+ method of your ``ConfigObj``. ``validate`` will return ``True`` if all -+ members are present, or a dictionary with True/False meaning -+ present/missing. (Whole missing sections will be replaced with ``False``) -+ """ -+ -+ def __init__(self): -+ self.baseErrorClass = ConfigObjError -+ -+ def check(self, check, member, missing=False): -+ """A dummy check method, always returns the value unchanged.""" -+ if missing: -+ raise self.baseErrorClass() -+ return member -+ -+ -+# Check / processing functions for options -+def flatten_errors(cfg, res, levels=None, results=None): -+ """ -+ An example function that will turn a nested dictionary of results -+ (as returned by ``ConfigObj.validate``) into a flat list. -+ -+ ``cfg`` is the ConfigObj instance being checked, ``res`` is the results -+ dictionary returned by ``validate``. -+ -+ (This is a recursive function, so you shouldn't use the ``levels`` or -+ ``results`` arguments - they are used by the function. -+ -+ Returns a list of keys that failed. Each member of the list is a tuple : -+ :: -+ -+ ([list of sections...], key, result) -+ -+ If ``validate`` was called with ``preserve_errors=False`` (the default) -+ then ``result`` will always be ``False``. -+ -+ *list of sections* is a flattened list of sections that the key was found -+ in. -+ -+ If the section was missing then key will be ``None``. -+ -+ If the value (or section) was missing then ``result`` will be ``False``. -+ -+ If ``validate`` was called with ``preserve_errors=True`` and a value -+ was present, but failed the check, then ``result`` will be the exception -+ object returned. You can use this as a string that describes the failure. -+ -+ For example *The value "3" is of the wrong type*. -+ -+ >>> import validate -+ >>> vtor = validate.Validator() -+ >>> my_ini = ''' -+ ... option1 = True -+ ... [section1] -+ ... option1 = True -+ ... [section2] -+ ... another_option = Probably -+ ... [section3] -+ ... another_option = True -+ ... [[section3b]] -+ ... value = 3 -+ ... value2 = a -+ ... value3 = 11 -+ ... ''' -+ >>> my_cfg = ''' -+ ... option1 = boolean() -+ ... option2 = boolean() -+ ... option3 = boolean(default=Bad_value) -+ ... [section1] -+ ... option1 = boolean() -+ ... option2 = boolean() -+ ... option3 = boolean(default=Bad_value) -+ ... [section2] -+ ... another_option = boolean() -+ ... [section3] -+ ... another_option = boolean() -+ ... [[section3b]] -+ ... value = integer -+ ... value2 = integer -+ ... value3 = integer(0, 10) -+ ... [[[section3b-sub]]] -+ ... value = string -+ ... [section4] -+ ... another_option = boolean() -+ ... ''' -+ >>> cs = my_cfg.split('\\n') -+ >>> ini = my_ini.split('\\n') -+ >>> cfg = ConfigObj(ini, configspec=cs) -+ >>> res = cfg.validate(vtor, preserve_errors=True) -+ >>> errors = [] -+ >>> for entry in flatten_errors(cfg, res): -+ ... section_list, key, error = entry -+ ... section_list.insert(0, '[root]') -+ ... if key is not None: -+ ... section_list.append(key) -+ ... else: -+ ... section_list.append('[missing]') -+ ... section_string = ', '.join(section_list) -+ ... errors.append((section_string, ' = ', error)) -+ >>> errors.sort() -+ >>> for entry in errors: -+ ... print entry[0], entry[1], (entry[2] or 0) -+ [root], option2 = 0 -+ [root], option3 = the value "Bad_value" is of the wrong type. -+ [root], section1, option2 = 0 -+ [root], section1, option3 = the value "Bad_value" is of the wrong type. -+ [root], section2, another_option = the value "Probably" is of the wrong type. -+ [root], section3, section3b, section3b-sub, [missing] = 0 -+ [root], section3, section3b, value2 = the value "a" is of the wrong type. -+ [root], section3, section3b, value3 = the value "11" is too big. -+ [root], section4, [missing] = 0 -+ """ -+ if levels is None: -+ # first time called -+ levels = [] -+ results = [] -+ if res is True: -+ return results -+ if res is False: -+ results.append((levels[:], None, False)) -+ if levels: -+ levels.pop() -+ return results -+ for (key, val) in res.items(): -+ if val == True: -+ continue -+ if isinstance(cfg.get(key), dict): -+ # Go down one level -+ levels.append(key) -+ flatten_errors(cfg[key], val, levels, results) -+ continue -+ results.append((levels[:], key, val)) -+ # -+ # Go up one level -+ if levels: -+ levels.pop() -+ # -+ return results -+ -+ -+"""*A programming language is a medium of expression.* - Paul Graham""" Index: ipython-0.10/IPython/external/configobj/__init__.py =================================================================== --- /dev/null @@ -6971,2687 +23,6 @@ Index: ipython-0.10/IPython/external/configobj/__init__.py + from configobj import * +except ImportError: + from _configobj import * -Index: ipython-0.10/IPython/external/configobj.py -=================================================================== ---- ipython-0.10.orig/IPython/external/configobj.py -+++ /dev/null -@@ -1,2501 +0,0 @@ --# configobj.py --# A config file reader/writer that supports nested sections in config files. --# Copyright (C) 2005-2008 Michael Foord, Nicola Larosa --# E-mail: fuzzyman AT voidspace DOT org DOT uk --# nico AT tekNico DOT net -- --# ConfigObj 4 --# http://www.voidspace.org.uk/python/configobj.html -- --# Released subject to the BSD License --# Please see http://www.voidspace.org.uk/python/license.shtml -- --# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml --# For information about bugfixes, updates and support, please join the --# ConfigObj mailing list: --# http://lists.sourceforge.net/lists/listinfo/configobj-develop --# Comments, suggestions and bug reports welcome. -- --from __future__ import generators -- --import sys --INTP_VER = sys.version_info[:2] --if INTP_VER < (2, 2): -- raise RuntimeError("Python v.2.2 or later needed") -- --import os, re --compiler = None --try: -- import compiler --except ImportError: -- # for IronPython -- pass --from types import StringTypes --from warnings import warn --try: -- from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE --except ImportError: -- # Python 2.2 does not have these -- # UTF-8 -- BOM_UTF8 = '\xef\xbb\xbf' -- # UTF-16, little endian -- BOM_UTF16_LE = '\xff\xfe' -- # UTF-16, big endian -- BOM_UTF16_BE = '\xfe\xff' -- if sys.byteorder == 'little': -- # UTF-16, native endianness -- BOM_UTF16 = BOM_UTF16_LE -- else: -- # UTF-16, native endianness -- BOM_UTF16 = BOM_UTF16_BE -- --# A dictionary mapping BOM to --# the encoding to decode with, and what to set the --# encoding attribute to. --BOMS = { -- BOM_UTF8: ('utf_8', None), -- BOM_UTF16_BE: ('utf16_be', 'utf_16'), -- BOM_UTF16_LE: ('utf16_le', 'utf_16'), -- BOM_UTF16: ('utf_16', 'utf_16'), -- } --# All legal variants of the BOM codecs. --# TODO: the list of aliases is not meant to be exhaustive, is there a --# better way ? --BOM_LIST = { -- 'utf_16': 'utf_16', -- 'u16': 'utf_16', -- 'utf16': 'utf_16', -- 'utf-16': 'utf_16', -- 'utf16_be': 'utf16_be', -- 'utf_16_be': 'utf16_be', -- 'utf-16be': 'utf16_be', -- 'utf16_le': 'utf16_le', -- 'utf_16_le': 'utf16_le', -- 'utf-16le': 'utf16_le', -- 'utf_8': 'utf_8', -- 'u8': 'utf_8', -- 'utf': 'utf_8', -- 'utf8': 'utf_8', -- 'utf-8': 'utf_8', -- } -- --# Map of encodings to the BOM to write. --BOM_SET = { -- 'utf_8': BOM_UTF8, -- 'utf_16': BOM_UTF16, -- 'utf16_be': BOM_UTF16_BE, -- 'utf16_le': BOM_UTF16_LE, -- None: BOM_UTF8 -- } -- -- --def match_utf8(encoding): -- return BOM_LIST.get(encoding.lower()) == 'utf_8' -- -- --# Quote strings used for writing values --squot = "'%s'" --dquot = '"%s"' --noquot = "%s" --wspace_plus = ' \r\t\n\v\t\'"' --tsquot = '"""%s"""' --tdquot = "'''%s'''" -- --try: -- enumerate --except NameError: -- def enumerate(obj): -- """enumerate for Python 2.2.""" -- i = -1 -- for item in obj: -- i += 1 -- yield i, item -- --try: -- True, False --except NameError: -- True, False = 1, 0 -- -- --__version__ = '4.5.2' -- --__revision__ = '$Id: configobj.py 156 2006-01-31 14:57:08Z fuzzyman $' -- --__docformat__ = "restructuredtext en" -- --__all__ = ( -- '__version__', -- 'DEFAULT_INDENT_TYPE', -- 'DEFAULT_INTERPOLATION', -- 'ConfigObjError', -- 'NestingError', -- 'ParseError', -- 'DuplicateError', -- 'ConfigspecError', -- 'ConfigObj', -- 'SimpleVal', -- 'InterpolationError', -- 'InterpolationLoopError', -- 'MissingInterpolationOption', -- 'RepeatSectionError', -- 'ReloadError', -- 'UnreprError', -- 'UnknownType', -- '__docformat__', -- 'flatten_errors', --) -- --DEFAULT_INTERPOLATION = 'configparser' --DEFAULT_INDENT_TYPE = ' ' --MAX_INTERPOL_DEPTH = 10 -- --OPTION_DEFAULTS = { -- 'interpolation': True, -- 'raise_errors': False, -- 'list_values': True, -- 'create_empty': False, -- 'file_error': False, -- 'configspec': None, -- 'stringify': True, -- # option may be set to one of ('', ' ', '\t') -- 'indent_type': None, -- 'encoding': None, -- 'default_encoding': None, -- 'unrepr': False, -- 'write_empty_values': False, --} -- -- -- --def getObj(s): -- s = "a=" + s -- if compiler is None: -- raise ImportError('compiler module not available') -- p = compiler.parse(s) -- return p.getChildren()[1].getChildren()[0].getChildren()[1] -- -- --class UnknownType(Exception): -- pass -- -- --class Builder(object): -- -- def build(self, o): -- m = getattr(self, 'build_' + o.__class__.__name__, None) -- if m is None: -- raise UnknownType(o.__class__.__name__) -- return m(o) -- -- def build_List(self, o): -- return map(self.build, o.getChildren()) -- -- def build_Const(self, o): -- return o.value -- -- def build_Dict(self, o): -- d = {} -- i = iter(map(self.build, o.getChildren())) -- for el in i: -- d[el] = i.next() -- return d -- -- def build_Tuple(self, o): -- return tuple(self.build_List(o)) -- -- def build_Name(self, o): -- if o.name == 'None': -- return None -- if o.name == 'True': -- return True -- if o.name == 'False': -- return False -- -- # An undefined Name -- raise UnknownType('Undefined Name') -- -- def build_Add(self, o): -- real, imag = map(self.build_Const, o.getChildren()) -- try: -- real = float(real) -- except TypeError: -- raise UnknownType('Add') -- if not isinstance(imag, complex) or imag.real != 0.0: -- raise UnknownType('Add') -- return real+imag -- -- def build_Getattr(self, o): -- parent = self.build(o.expr) -- return getattr(parent, o.attrname) -- -- def build_UnarySub(self, o): -- return -self.build_Const(o.getChildren()[0]) -- -- def build_UnaryAdd(self, o): -- return self.build_Const(o.getChildren()[0]) -- -- --_builder = Builder() -- -- --def unrepr(s): -- if not s: -- return s -- return _builder.build(getObj(s)) -- -- -- --class ConfigObjError(SyntaxError): -- """ -- This is the base class for all errors that ConfigObj raises. -- It is a subclass of SyntaxError. -- """ -- def __init__(self, message='', line_number=None, line=''): -- self.line = line -- self.line_number = line_number -- self.message = message -- SyntaxError.__init__(self, message) -- -- --class NestingError(ConfigObjError): -- """ -- This error indicates a level of nesting that doesn't match. -- """ -- -- --class ParseError(ConfigObjError): -- """ -- This error indicates that a line is badly written. -- It is neither a valid ``key = value`` line, -- nor a valid section marker line. -- """ -- -- --class ReloadError(IOError): -- """ -- A 'reload' operation failed. -- This exception is a subclass of ``IOError``. -- """ -- def __init__(self): -- IOError.__init__(self, 'reload failed, filename is not set.') -- -- --class DuplicateError(ConfigObjError): -- """ -- The keyword or section specified already exists. -- """ -- -- --class ConfigspecError(ConfigObjError): -- """ -- An error occured whilst parsing a configspec. -- """ -- -- --class InterpolationError(ConfigObjError): -- """Base class for the two interpolation errors.""" -- -- --class InterpolationLoopError(InterpolationError): -- """Maximum interpolation depth exceeded in string interpolation.""" -- -- def __init__(self, option): -- InterpolationError.__init__( -- self, -- 'interpolation loop detected in value "%s".' % option) -- -- --class RepeatSectionError(ConfigObjError): -- """ -- This error indicates additional sections in a section with a -- ``__many__`` (repeated) section. -- """ -- -- --class MissingInterpolationOption(InterpolationError): -- """A value specified for interpolation was missing.""" -- -- def __init__(self, option): -- InterpolationError.__init__( -- self, -- 'missing option "%s" in interpolation.' % option) -- -- --class UnreprError(ConfigObjError): -- """An error parsing in unrepr mode.""" -- -- -- --class InterpolationEngine(object): -- """ -- A helper class to help perform string interpolation. -- -- This class is an abstract base class; its descendants perform -- the actual work. -- """ -- -- # compiled regexp to use in self.interpolate() -- _KEYCRE = re.compile(r"%\(([^)]*)\)s") -- -- def __init__(self, section): -- # the Section instance that "owns" this engine -- self.section = section -- -- -- def interpolate(self, key, value): -- def recursive_interpolate(key, value, section, backtrail): -- """The function that does the actual work. -- -- ``value``: the string we're trying to interpolate. -- ``section``: the section in which that string was found -- ``backtrail``: a dict to keep track of where we've been, -- to detect and prevent infinite recursion loops -- -- This is similar to a depth-first-search algorithm. -- """ -- # Have we been here already? -- if backtrail.has_key((key, section.name)): -- # Yes - infinite loop detected -- raise InterpolationLoopError(key) -- # Place a marker on our backtrail so we won't come back here again -- backtrail[(key, section.name)] = 1 -- -- # Now start the actual work -- match = self._KEYCRE.search(value) -- while match: -- # The actual parsing of the match is implementation-dependent, -- # so delegate to our helper function -- k, v, s = self._parse_match(match) -- if k is None: -- # That's the signal that no further interpolation is needed -- replacement = v -- else: -- # Further interpolation may be needed to obtain final value -- replacement = recursive_interpolate(k, v, s, backtrail) -- # Replace the matched string with its final value -- start, end = match.span() -- value = ''.join((value[:start], replacement, value[end:])) -- new_search_start = start + len(replacement) -- # Pick up the next interpolation key, if any, for next time -- # through the while loop -- match = self._KEYCRE.search(value, new_search_start) -- -- # Now safe to come back here again; remove marker from backtrail -- del backtrail[(key, section.name)] -- -- return value -- -- # Back in interpolate(), all we have to do is kick off the recursive -- # function with appropriate starting values -- value = recursive_interpolate(key, value, self.section, {}) -- return value -- -- -- def _fetch(self, key): -- """Helper function to fetch values from owning section. -- -- Returns a 2-tuple: the value, and the section where it was found. -- """ -- # switch off interpolation before we try and fetch anything ! -- save_interp = self.section.main.interpolation -- self.section.main.interpolation = False -- -- # Start at section that "owns" this InterpolationEngine -- current_section = self.section -- while True: -- # try the current section first -- val = current_section.get(key) -- if val is not None: -- break -- # try "DEFAULT" next -- val = current_section.get('DEFAULT', {}).get(key) -- if val is not None: -- break -- # move up to parent and try again -- # top-level's parent is itself -- if current_section.parent is current_section: -- # reached top level, time to give up -- break -- current_section = current_section.parent -- -- # restore interpolation to previous value before returning -- self.section.main.interpolation = save_interp -- if val is None: -- raise MissingInterpolationOption(key) -- return val, current_section -- -- -- def _parse_match(self, match): -- """Implementation-dependent helper function. -- -- Will be passed a match object corresponding to the interpolation -- key we just found (e.g., "%(foo)s" or "$foo"). Should look up that -- key in the appropriate config file section (using the ``_fetch()`` -- helper function) and return a 3-tuple: (key, value, section) -- -- ``key`` is the name of the key we're looking for -- ``value`` is the value found for that key -- ``section`` is a reference to the section where it was found -- -- ``key`` and ``section`` should be None if no further -- interpolation should be performed on the resulting value -- (e.g., if we interpolated "$$" and returned "$"). -- """ -- raise NotImplementedError() -- -- -- --class ConfigParserInterpolation(InterpolationEngine): -- """Behaves like ConfigParser.""" -- _KEYCRE = re.compile(r"%\(([^)]*)\)s") -- -- def _parse_match(self, match): -- key = match.group(1) -- value, section = self._fetch(key) -- return key, value, section -- -- -- --class TemplateInterpolation(InterpolationEngine): -- """Behaves like string.Template.""" -- _delimiter = '$' -- _KEYCRE = re.compile(r""" -- \$(?: -- (?P\$) | # Two $ signs -- (?P[_a-z][_a-z0-9]*) | # $name format -- {(?P[^}]*)} # ${name} format -- ) -- """, re.IGNORECASE | re.VERBOSE) -- -- def _parse_match(self, match): -- # Valid name (in or out of braces): fetch value from section -- key = match.group('named') or match.group('braced') -- if key is not None: -- value, section = self._fetch(key) -- return key, value, section -- # Escaped delimiter (e.g., $$): return single delimiter -- if match.group('escaped') is not None: -- # Return None for key and section to indicate it's time to stop -- return None, self._delimiter, None -- # Anything else: ignore completely, just return it unchanged -- return None, match.group(), None -- -- --interpolation_engines = { -- 'configparser': ConfigParserInterpolation, -- 'template': TemplateInterpolation, --} -- -- -- --class Section(dict): -- """ -- A dictionary-like object that represents a section in a config file. -- -- It does string interpolation if the 'interpolation' attribute -- of the 'main' object is set to True. -- -- Interpolation is tried first from this object, then from the 'DEFAULT' -- section of this object, next from the parent and its 'DEFAULT' section, -- and so on until the main object is reached. -- -- A Section will behave like an ordered dictionary - following the -- order of the ``scalars`` and ``sections`` attributes. -- You can use this to change the order of members. -- -- Iteration follows the order: scalars, then sections. -- """ -- -- def __init__(self, parent, depth, main, indict=None, name=None): -- """ -- * parent is the section above -- * depth is the depth level of this section -- * main is the main ConfigObj -- * indict is a dictionary to initialise the section with -- """ -- if indict is None: -- indict = {} -- dict.__init__(self) -- # used for nesting level *and* interpolation -- self.parent = parent -- # used for the interpolation attribute -- self.main = main -- # level of nesting depth of this Section -- self.depth = depth -- # purely for information -- self.name = name -- # -- self._initialise() -- # we do this explicitly so that __setitem__ is used properly -- # (rather than just passing to ``dict.__init__``) -- for entry, value in indict.iteritems(): -- self[entry] = value -- -- -- def _initialise(self): -- # the sequence of scalar values in this Section -- self.scalars = [] -- # the sequence of sections in this Section -- self.sections = [] -- # for comments :-) -- self.comments = {} -- self.inline_comments = {} -- # for the configspec -- self.configspec = {} -- self._order = [] -- self._configspec_comments = {} -- self._configspec_inline_comments = {} -- self._cs_section_comments = {} -- self._cs_section_inline_comments = {} -- # for defaults -- self.defaults = [] -- self.default_values = {} -- -- -- def _interpolate(self, key, value): -- try: -- # do we already have an interpolation engine? -- engine = self._interpolation_engine -- except AttributeError: -- # not yet: first time running _interpolate(), so pick the engine -- name = self.main.interpolation -- if name == True: # note that "if name:" would be incorrect here -- # backwards-compatibility: interpolation=True means use default -- name = DEFAULT_INTERPOLATION -- name = name.lower() # so that "Template", "template", etc. all work -- class_ = interpolation_engines.get(name, None) -- if class_ is None: -- # invalid value for self.main.interpolation -- self.main.interpolation = False -- return value -- else: -- # save reference to engine so we don't have to do this again -- engine = self._interpolation_engine = class_(self) -- # let the engine do the actual work -- return engine.interpolate(key, value) -- -- -- def __getitem__(self, key): -- """Fetch the item and do string interpolation.""" -- val = dict.__getitem__(self, key) -- if self.main.interpolation and isinstance(val, StringTypes): -- return self._interpolate(key, val) -- return val -- -- -- def __setitem__(self, key, value, unrepr=False): -- """ -- Correctly set a value. -- -- Making dictionary values Section instances. -- (We have to special case 'Section' instances - which are also dicts) -- -- Keys must be strings. -- Values need only be strings (or lists of strings) if -- ``main.stringify`` is set. -- -- `unrepr`` must be set when setting a value to a dictionary, without -- creating a new sub-section. -- """ -- if not isinstance(key, StringTypes): -- raise ValueError('The key "%s" is not a string.' % key) -- -- # add the comment -- if not self.comments.has_key(key): -- self.comments[key] = [] -- self.inline_comments[key] = '' -- # remove the entry from defaults -- if key in self.defaults: -- self.defaults.remove(key) -- # -- if isinstance(value, Section): -- if not self.has_key(key): -- self.sections.append(key) -- dict.__setitem__(self, key, value) -- elif isinstance(value, dict) and not unrepr: -- # First create the new depth level, -- # then create the section -- if not self.has_key(key): -- self.sections.append(key) -- new_depth = self.depth + 1 -- dict.__setitem__( -- self, -- key, -- Section( -- self, -- new_depth, -- self.main, -- indict=value, -- name=key)) -- else: -- if not self.has_key(key): -- self.scalars.append(key) -- if not self.main.stringify: -- if isinstance(value, StringTypes): -- pass -- elif isinstance(value, (list, tuple)): -- for entry in value: -- if not isinstance(entry, StringTypes): -- raise TypeError('Value is not a string "%s".' % entry) -- else: -- raise TypeError('Value is not a string "%s".' % value) -- dict.__setitem__(self, key, value) -- -- -- def __delitem__(self, key): -- """Remove items from the sequence when deleting.""" -- dict. __delitem__(self, key) -- if key in self.scalars: -- self.scalars.remove(key) -- else: -- self.sections.remove(key) -- del self.comments[key] -- del self.inline_comments[key] -- -- -- def get(self, key, default=None): -- """A version of ``get`` that doesn't bypass string interpolation.""" -- try: -- return self[key] -- except KeyError: -- return default -- -- -- def update(self, indict): -- """ -- A version of update that uses our ``__setitem__``. -- """ -- for entry in indict: -- self[entry] = indict[entry] -- -- -- def pop(self, key, *args): -- """ -- 'D.pop(k[,d]) -> v, remove specified key and return the corresponding value. -- If key is not found, d is returned if given, otherwise KeyError is raised' -- """ -- val = dict.pop(self, key, *args) -- if key in self.scalars: -- del self.comments[key] -- del self.inline_comments[key] -- self.scalars.remove(key) -- elif key in self.sections: -- del self.comments[key] -- del self.inline_comments[key] -- self.sections.remove(key) -- if self.main.interpolation and isinstance(val, StringTypes): -- return self._interpolate(key, val) -- return val -- -- -- def popitem(self): -- """Pops the first (key,val)""" -- sequence = (self.scalars + self.sections) -- if not sequence: -- raise KeyError(": 'popitem(): dictionary is empty'") -- key = sequence[0] -- val = self[key] -- del self[key] -- return key, val -- -- -- def clear(self): -- """ -- A version of clear that also affects scalars/sections -- Also clears comments and configspec. -- -- Leaves other attributes alone : -- depth/main/parent are not affected -- """ -- dict.clear(self) -- self.scalars = [] -- self.sections = [] -- self.comments = {} -- self.inline_comments = {} -- self.configspec = {} -- -- -- def setdefault(self, key, default=None): -- """A version of setdefault that sets sequence if appropriate.""" -- try: -- return self[key] -- except KeyError: -- self[key] = default -- return self[key] -- -- -- def items(self): -- """D.items() -> list of D's (key, value) pairs, as 2-tuples""" -- return zip((self.scalars + self.sections), self.values()) -- -- -- def keys(self): -- """D.keys() -> list of D's keys""" -- return (self.scalars + self.sections) -- -- -- def values(self): -- """D.values() -> list of D's values""" -- return [self[key] for key in (self.scalars + self.sections)] -- -- -- def iteritems(self): -- """D.iteritems() -> an iterator over the (key, value) items of D""" -- return iter(self.items()) -- -- -- def iterkeys(self): -- """D.iterkeys() -> an iterator over the keys of D""" -- return iter((self.scalars + self.sections)) -- -- __iter__ = iterkeys -- -- -- def itervalues(self): -- """D.itervalues() -> an iterator over the values of D""" -- return iter(self.values()) -- -- -- def __repr__(self): -- """x.__repr__() <==> repr(x)""" -- return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(self[key]))) -- for key in (self.scalars + self.sections)]) -- -- __str__ = __repr__ -- __str__.__doc__ = "x.__str__() <==> str(x)" -- -- -- # Extra methods - not in a normal dictionary -- -- def dict(self): -- """ -- Return a deepcopy of self as a dictionary. -- -- All members that are ``Section`` instances are recursively turned to -- ordinary dictionaries - by calling their ``dict`` method. -- -- >>> n = a.dict() -- >>> n == a -- 1 -- >>> n is a -- 0 -- """ -- newdict = {} -- for entry in self: -- this_entry = self[entry] -- if isinstance(this_entry, Section): -- this_entry = this_entry.dict() -- elif isinstance(this_entry, list): -- # create a copy rather than a reference -- this_entry = list(this_entry) -- elif isinstance(this_entry, tuple): -- # create a copy rather than a reference -- this_entry = tuple(this_entry) -- newdict[entry] = this_entry -- return newdict -- -- -- def merge(self, indict): -- """ -- A recursive update - useful for merging config files. -- -- >>> a = '''[section1] -- ... option1 = True -- ... [[subsection]] -- ... more_options = False -- ... # end of file'''.splitlines() -- >>> b = '''# File is user.ini -- ... [section1] -- ... option1 = False -- ... # end of file'''.splitlines() -- >>> c1 = ConfigObj(b) -- >>> c2 = ConfigObj(a) -- >>> c2.merge(c1) -- >>> c2 -- {'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}} -- """ -- for key, val in indict.items(): -- if (key in self and isinstance(self[key], dict) and -- isinstance(val, dict)): -- self[key].merge(val) -- else: -- self[key] = val -- -- -- def rename(self, oldkey, newkey): -- """ -- Change a keyname to another, without changing position in sequence. -- -- Implemented so that transformations can be made on keys, -- as well as on values. (used by encode and decode) -- -- Also renames comments. -- """ -- if oldkey in self.scalars: -- the_list = self.scalars -- elif oldkey in self.sections: -- the_list = self.sections -- else: -- raise KeyError('Key "%s" not found.' % oldkey) -- pos = the_list.index(oldkey) -- # -- val = self[oldkey] -- dict.__delitem__(self, oldkey) -- dict.__setitem__(self, newkey, val) -- the_list.remove(oldkey) -- the_list.insert(pos, newkey) -- comm = self.comments[oldkey] -- inline_comment = self.inline_comments[oldkey] -- del self.comments[oldkey] -- del self.inline_comments[oldkey] -- self.comments[newkey] = comm -- self.inline_comments[newkey] = inline_comment -- -- -- def walk(self, function, raise_errors=True, -- call_on_sections=False, **keywargs): -- """ -- Walk every member and call a function on the keyword and value. -- -- Return a dictionary of the return values -- -- If the function raises an exception, raise the errror -- unless ``raise_errors=False``, in which case set the return value to -- ``False``. -- -- Any unrecognised keyword arguments you pass to walk, will be pased on -- to the function you pass in. -- -- Note: if ``call_on_sections`` is ``True`` then - on encountering a -- subsection, *first* the function is called for the *whole* subsection, -- and then recurses into it's members. This means your function must be -- able to handle strings, dictionaries and lists. This allows you -- to change the key of subsections as well as for ordinary members. The -- return value when called on the whole subsection has to be discarded. -- -- See the encode and decode methods for examples, including functions. -- -- .. caution:: -- -- You can use ``walk`` to transform the names of members of a section -- but you mustn't add or delete members. -- -- >>> config = '''[XXXXsection] -- ... XXXXkey = XXXXvalue'''.splitlines() -- >>> cfg = ConfigObj(config) -- >>> cfg -- {'XXXXsection': {'XXXXkey': 'XXXXvalue'}} -- >>> def transform(section, key): -- ... val = section[key] -- ... newkey = key.replace('XXXX', 'CLIENT1') -- ... section.rename(key, newkey) -- ... if isinstance(val, (tuple, list, dict)): -- ... pass -- ... else: -- ... val = val.replace('XXXX', 'CLIENT1') -- ... section[newkey] = val -- >>> cfg.walk(transform, call_on_sections=True) -- {'CLIENT1section': {'CLIENT1key': None}} -- >>> cfg -- {'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}} -- """ -- out = {} -- # scalars first -- for i in range(len(self.scalars)): -- entry = self.scalars[i] -- try: -- val = function(self, entry, **keywargs) -- # bound again in case name has changed -- entry = self.scalars[i] -- out[entry] = val -- except Exception: -- if raise_errors: -- raise -- else: -- entry = self.scalars[i] -- out[entry] = False -- # then sections -- for i in range(len(self.sections)): -- entry = self.sections[i] -- if call_on_sections: -- try: -- function(self, entry, **keywargs) -- except Exception: -- if raise_errors: -- raise -- else: -- entry = self.sections[i] -- out[entry] = False -- # bound again in case name has changed -- entry = self.sections[i] -- # previous result is discarded -- out[entry] = self[entry].walk( -- function, -- raise_errors=raise_errors, -- call_on_sections=call_on_sections, -- **keywargs) -- return out -- -- -- def decode(self, encoding): -- """ -- Decode all strings and values to unicode, using the specified encoding. -- -- Works with subsections and list values. -- -- Uses the ``walk`` method. -- -- Testing ``encode`` and ``decode``. -- >>> m = ConfigObj(a) -- >>> m.decode('ascii') -- >>> def testuni(val): -- ... for entry in val: -- ... if not isinstance(entry, unicode): -- ... print >> sys.stderr, type(entry) -- ... raise AssertionError, 'decode failed.' -- ... if isinstance(val[entry], dict): -- ... testuni(val[entry]) -- ... elif not isinstance(val[entry], unicode): -- ... raise AssertionError, 'decode failed.' -- >>> testuni(m) -- >>> m.encode('ascii') -- >>> a == m -- 1 -- """ -- warn('use of ``decode`` is deprecated.', DeprecationWarning) -- def decode(section, key, encoding=encoding, warn=True): -- """ """ -- val = section[key] -- if isinstance(val, (list, tuple)): -- newval = [] -- for entry in val: -- newval.append(entry.decode(encoding)) -- elif isinstance(val, dict): -- newval = val -- else: -- newval = val.decode(encoding) -- newkey = key.decode(encoding) -- section.rename(key, newkey) -- section[newkey] = newval -- # using ``call_on_sections`` allows us to modify section names -- self.walk(decode, call_on_sections=True) -- -- -- def encode(self, encoding): -- """ -- Encode all strings and values from unicode, -- using the specified encoding. -- -- Works with subsections and list values. -- Uses the ``walk`` method. -- """ -- warn('use of ``encode`` is deprecated.', DeprecationWarning) -- def encode(section, key, encoding=encoding): -- """ """ -- val = section[key] -- if isinstance(val, (list, tuple)): -- newval = [] -- for entry in val: -- newval.append(entry.encode(encoding)) -- elif isinstance(val, dict): -- newval = val -- else: -- newval = val.encode(encoding) -- newkey = key.encode(encoding) -- section.rename(key, newkey) -- section[newkey] = newval -- self.walk(encode, call_on_sections=True) -- -- -- def istrue(self, key): -- """A deprecated version of ``as_bool``.""" -- warn('use of ``istrue`` is deprecated. Use ``as_bool`` method ' -- 'instead.', DeprecationWarning) -- return self.as_bool(key) -- -- -- def as_bool(self, key): -- """ -- Accepts a key as input. The corresponding value must be a string or -- the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to -- retain compatibility with Python 2.2. -- -- If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns -- ``True``. -- -- If the string is one of ``False``, ``Off``, ``No``, or ``0`` it returns -- ``False``. -- -- ``as_bool`` is not case sensitive. -- -- Any other input will raise a ``ValueError``. -- -- >>> a = ConfigObj() -- >>> a['a'] = 'fish' -- >>> a.as_bool('a') -- Traceback (most recent call last): -- ValueError: Value "fish" is neither True nor False -- >>> a['b'] = 'True' -- >>> a.as_bool('b') -- 1 -- >>> a['b'] = 'off' -- >>> a.as_bool('b') -- 0 -- """ -- val = self[key] -- if val == True: -- return True -- elif val == False: -- return False -- else: -- try: -- if not isinstance(val, StringTypes): -- # TODO: Why do we raise a KeyError here? -- raise KeyError() -- else: -- return self.main._bools[val.lower()] -- except KeyError: -- raise ValueError('Value "%s" is neither True nor False' % val) -- -- -- def as_int(self, key): -- """ -- A convenience method which coerces the specified value to an integer. -- -- If the value is an invalid literal for ``int``, a ``ValueError`` will -- be raised. -- -- >>> a = ConfigObj() -- >>> a['a'] = 'fish' -- >>> a.as_int('a') -- Traceback (most recent call last): -- ValueError: invalid literal for int(): fish -- >>> a['b'] = '1' -- >>> a.as_int('b') -- 1 -- >>> a['b'] = '3.2' -- >>> a.as_int('b') -- Traceback (most recent call last): -- ValueError: invalid literal for int(): 3.2 -- """ -- return int(self[key]) -- -- -- def as_float(self, key): -- """ -- A convenience method which coerces the specified value to a float. -- -- If the value is an invalid literal for ``float``, a ``ValueError`` will -- be raised. -- -- >>> a = ConfigObj() -- >>> a['a'] = 'fish' -- >>> a.as_float('a') -- Traceback (most recent call last): -- ValueError: invalid literal for float(): fish -- >>> a['b'] = '1' -- >>> a.as_float('b') -- 1.0 -- >>> a['b'] = '3.2' -- >>> a.as_float('b') -- 3.2000000000000002 -- """ -- return float(self[key]) -- -- -- def restore_default(self, key): -- """ -- Restore (and return) default value for the specified key. -- -- This method will only work for a ConfigObj that was created -- with a configspec and has been validated. -- -- If there is no default value for this key, ``KeyError`` is raised. -- """ -- default = self.default_values[key] -- dict.__setitem__(self, key, default) -- if key not in self.defaults: -- self.defaults.append(key) -- return default -- -- -- def restore_defaults(self): -- """ -- Recursively restore default values to all members -- that have them. -- -- This method will only work for a ConfigObj that was created -- with a configspec and has been validated. -- -- It doesn't delete or modify entries without default values. -- """ -- for key in self.default_values: -- self.restore_default(key) -- -- for section in self.sections: -- self[section].restore_defaults() -- -- --class ConfigObj(Section): -- """An object to read, create, and write config files.""" -- -- _keyword = re.compile(r'''^ # line start -- (\s*) # indentation -- ( # keyword -- (?:".*?")| # double quotes -- (?:'.*?')| # single quotes -- (?:[^'"=].*?) # no quotes -- ) -- \s*=\s* # divider -- (.*) # value (including list values and comments) -- $ # line end -- ''', -- re.VERBOSE) -- -- _sectionmarker = re.compile(r'''^ -- (\s*) # 1: indentation -- ((?:\[\s*)+) # 2: section marker open -- ( # 3: section name open -- (?:"\s*\S.*?\s*")| # at least one non-space with double quotes -- (?:'\s*\S.*?\s*')| # at least one non-space with single quotes -- (?:[^'"\s].*?) # at least one non-space unquoted -- ) # section name close -- ((?:\s*\])+) # 4: section marker close -- \s*(\#.*)? # 5: optional comment -- $''', -- re.VERBOSE) -- -- # this regexp pulls list values out as a single string -- # or single values and comments -- # FIXME: this regex adds a '' to the end of comma terminated lists -- # workaround in ``_handle_value`` -- _valueexp = re.compile(r'''^ -- (?: -- (?: -- ( -- (?: -- (?: -- (?:".*?")| # double quotes -- (?:'.*?')| # single quotes -- (?:[^'",\#][^,\#]*?) # unquoted -- ) -- \s*,\s* # comma -- )* # match all list items ending in a comma (if any) -- ) -- ( -- (?:".*?")| # double quotes -- (?:'.*?')| # single quotes -- (?:[^'",\#\s][^,]*?)| # unquoted -- (?:(? 1: -- msg = "Parsing failed with several errors.\nFirst error %s" % info -- error = ConfigObjError(msg) -- else: -- error = self._errors[0] -- # set the errors attribute; it's a list of tuples: -- # (error_type, message, line_number) -- error.errors = self._errors -- # set the config attribute -- error.config = self -- raise error -- # delete private attributes -- del self._errors -- -- if configspec is None: -- self.configspec = None -- else: -- self._handle_configspec(configspec) -- -- -- def _initialise(self, options=None): -- if options is None: -- options = OPTION_DEFAULTS -- -- # initialise a few variables -- self.filename = None -- self._errors = [] -- self.raise_errors = options['raise_errors'] -- self.interpolation = options['interpolation'] -- self.list_values = options['list_values'] -- self.create_empty = options['create_empty'] -- self.file_error = options['file_error'] -- self.stringify = options['stringify'] -- self.indent_type = options['indent_type'] -- self.encoding = options['encoding'] -- self.default_encoding = options['default_encoding'] -- self.BOM = False -- self.newlines = None -- self.write_empty_values = options['write_empty_values'] -- self.unrepr = options['unrepr'] -- -- self.initial_comment = [] -- self.final_comment = [] -- self.configspec = {} -- -- # Clear section attributes as well -- Section._initialise(self) -- -- -- def __repr__(self): -- return ('ConfigObj({%s})' % -- ', '.join([('%s: %s' % (repr(key), repr(self[key]))) -- for key in (self.scalars + self.sections)])) -- -- -- def _handle_bom(self, infile): -- """ -- Handle any BOM, and decode if necessary. -- -- If an encoding is specified, that *must* be used - but the BOM should -- still be removed (and the BOM attribute set). -- -- (If the encoding is wrongly specified, then a BOM for an alternative -- encoding won't be discovered or removed.) -- -- If an encoding is not specified, UTF8 or UTF16 BOM will be detected and -- removed. The BOM attribute will be set. UTF16 will be decoded to -- unicode. -- -- NOTE: This method must not be called with an empty ``infile``. -- -- Specifying the *wrong* encoding is likely to cause a -- ``UnicodeDecodeError``. -- -- ``infile`` must always be returned as a list of lines, but may be -- passed in as a single string. -- """ -- if ((self.encoding is not None) and -- (self.encoding.lower() not in BOM_LIST)): -- # No need to check for a BOM -- # the encoding specified doesn't have one -- # just decode -- return self._decode(infile, self.encoding) -- -- if isinstance(infile, (list, tuple)): -- line = infile[0] -- else: -- line = infile -- if self.encoding is not None: -- # encoding explicitly supplied -- # And it could have an associated BOM -- # TODO: if encoding is just UTF16 - we ought to check for both -- # TODO: big endian and little endian versions. -- enc = BOM_LIST[self.encoding.lower()] -- if enc == 'utf_16': -- # For UTF16 we try big endian and little endian -- for BOM, (encoding, final_encoding) in BOMS.items(): -- if not final_encoding: -- # skip UTF8 -- continue -- if infile.startswith(BOM): -- ### BOM discovered -- ##self.BOM = True -- # Don't need to remove BOM -- return self._decode(infile, encoding) -- -- # If we get this far, will *probably* raise a DecodeError -- # As it doesn't appear to start with a BOM -- return self._decode(infile, self.encoding) -- -- # Must be UTF8 -- BOM = BOM_SET[enc] -- if not line.startswith(BOM): -- return self._decode(infile, self.encoding) -- -- newline = line[len(BOM):] -- -- # BOM removed -- if isinstance(infile, (list, tuple)): -- infile[0] = newline -- else: -- infile = newline -- self.BOM = True -- return self._decode(infile, self.encoding) -- -- # No encoding specified - so we need to check for UTF8/UTF16 -- for BOM, (encoding, final_encoding) in BOMS.items(): -- if not line.startswith(BOM): -- continue -- else: -- # BOM discovered -- self.encoding = final_encoding -- if not final_encoding: -- self.BOM = True -- # UTF8 -- # remove BOM -- newline = line[len(BOM):] -- if isinstance(infile, (list, tuple)): -- infile[0] = newline -- else: -- infile = newline -- # UTF8 - don't decode -- if isinstance(infile, StringTypes): -- return infile.splitlines(True) -- else: -- return infile -- # UTF16 - have to decode -- return self._decode(infile, encoding) -- -- # No BOM discovered and no encoding specified, just return -- if isinstance(infile, StringTypes): -- # infile read from a file will be a single string -- return infile.splitlines(True) -- return infile -- -- -- def _a_to_u(self, aString): -- """Decode ASCII strings to unicode if a self.encoding is specified.""" -- if self.encoding: -- return aString.decode('ascii') -- else: -- return aString -- -- -- def _decode(self, infile, encoding): -- """ -- Decode infile to unicode. Using the specified encoding. -- -- if is a string, it also needs converting to a list. -- """ -- if isinstance(infile, StringTypes): -- # can't be unicode -- # NOTE: Could raise a ``UnicodeDecodeError`` -- return infile.decode(encoding).splitlines(True) -- for i, line in enumerate(infile): -- if not isinstance(line, unicode): -- # NOTE: The isinstance test here handles mixed lists of unicode/string -- # NOTE: But the decode will break on any non-string values -- # NOTE: Or could raise a ``UnicodeDecodeError`` -- infile[i] = line.decode(encoding) -- return infile -- -- -- def _decode_element(self, line): -- """Decode element to unicode if necessary.""" -- if not self.encoding: -- return line -- if isinstance(line, str) and self.default_encoding: -- return line.decode(self.default_encoding) -- return line -- -- -- def _str(self, value): -- """ -- Used by ``stringify`` within validate, to turn non-string values -- into strings. -- """ -- if not isinstance(value, StringTypes): -- return str(value) -- else: -- return value -- -- -- def _parse(self, infile): -- """Actually parse the config file.""" -- temp_list_values = self.list_values -- if self.unrepr: -- self.list_values = False -- -- comment_list = [] -- done_start = False -- this_section = self -- maxline = len(infile) - 1 -- cur_index = -1 -- reset_comment = False -- -- while cur_index < maxline: -- if reset_comment: -- comment_list = [] -- cur_index += 1 -- line = infile[cur_index] -- sline = line.strip() -- # do we have anything on the line ? -- if not sline or sline.startswith('#'): -- reset_comment = False -- comment_list.append(line) -- continue -- -- if not done_start: -- # preserve initial comment -- self.initial_comment = comment_list -- comment_list = [] -- done_start = True -- -- reset_comment = True -- # first we check if it's a section marker -- mat = self._sectionmarker.match(line) -- if mat is not None: -- # is a section line -- (indent, sect_open, sect_name, sect_close, comment) = mat.groups() -- if indent and (self.indent_type is None): -- self.indent_type = indent -- cur_depth = sect_open.count('[') -- if cur_depth != sect_close.count(']'): -- self._handle_error("Cannot compute the section depth at line %s.", -- NestingError, infile, cur_index) -- continue -- -- if cur_depth < this_section.depth: -- # the new section is dropping back to a previous level -- try: -- parent = self._match_depth(this_section, -- cur_depth).parent -- except SyntaxError: -- self._handle_error("Cannot compute nesting level at line %s.", -- NestingError, infile, cur_index) -- continue -- elif cur_depth == this_section.depth: -- # the new section is a sibling of the current section -- parent = this_section.parent -- elif cur_depth == this_section.depth + 1: -- # the new section is a child the current section -- parent = this_section -- else: -- self._handle_error("Section too nested at line %s.", -- NestingError, infile, cur_index) -- -- sect_name = self._unquote(sect_name) -- if parent.has_key(sect_name): -- self._handle_error('Duplicate section name at line %s.', -- DuplicateError, infile, cur_index) -- continue -- -- # create the new section -- this_section = Section( -- parent, -- cur_depth, -- self, -- name=sect_name) -- parent[sect_name] = this_section -- parent.inline_comments[sect_name] = comment -- parent.comments[sect_name] = comment_list -- continue -- # -- # it's not a section marker, -- # so it should be a valid ``key = value`` line -- mat = self._keyword.match(line) -- if mat is None: -- # it neither matched as a keyword -- # or a section marker -- self._handle_error( -- 'Invalid line at line "%s".', -- ParseError, infile, cur_index) -- else: -- # is a keyword value -- # value will include any inline comment -- (indent, key, value) = mat.groups() -- if indent and (self.indent_type is None): -- self.indent_type = indent -- # check for a multiline value -- if value[:3] in ['"""', "'''"]: -- try: -- (value, comment, cur_index) = self._multiline( -- value, infile, cur_index, maxline) -- except SyntaxError: -- self._handle_error( -- 'Parse error in value at line %s.', -- ParseError, infile, cur_index) -- continue -- else: -- if self.unrepr: -- comment = '' -- try: -- value = unrepr(value) -- except Exception, e: -- if type(e) == UnknownType: -- msg = 'Unknown name or type in value at line %s.' -- else: -- msg = 'Parse error in value at line %s.' -- self._handle_error(msg, UnreprError, infile, -- cur_index) -- continue -- else: -- if self.unrepr: -- comment = '' -- try: -- value = unrepr(value) -- except Exception, e: -- if isinstance(e, UnknownType): -- msg = 'Unknown name or type in value at line %s.' -- else: -- msg = 'Parse error in value at line %s.' -- self._handle_error(msg, UnreprError, infile, -- cur_index) -- continue -- else: -- # extract comment and lists -- try: -- (value, comment) = self._handle_value(value) -- except SyntaxError: -- self._handle_error( -- 'Parse error in value at line %s.', -- ParseError, infile, cur_index) -- continue -- # -- key = self._unquote(key) -- if this_section.has_key(key): -- self._handle_error( -- 'Duplicate keyword name at line %s.', -- DuplicateError, infile, cur_index) -- continue -- # add the key. -- # we set unrepr because if we have got this far we will never -- # be creating a new section -- this_section.__setitem__(key, value, unrepr=True) -- this_section.inline_comments[key] = comment -- this_section.comments[key] = comment_list -- continue -- # -- if self.indent_type is None: -- # no indentation used, set the type accordingly -- self.indent_type = '' -- -- # preserve the final comment -- if not self and not self.initial_comment: -- self.initial_comment = comment_list -- elif not reset_comment: -- self.final_comment = comment_list -- self.list_values = temp_list_values -- -- -- def _match_depth(self, sect, depth): -- """ -- Given a section and a depth level, walk back through the sections -- parents to see if the depth level matches a previous section. -- -- Return a reference to the right section, -- or raise a SyntaxError. -- """ -- while depth < sect.depth: -- if sect is sect.parent: -- # we've reached the top level already -- raise SyntaxError() -- sect = sect.parent -- if sect.depth == depth: -- return sect -- # shouldn't get here -- raise SyntaxError() -- -- -- def _handle_error(self, text, ErrorClass, infile, cur_index): -- """ -- Handle an error according to the error settings. -- -- Either raise the error or store it. -- The error will have occured at ``cur_index`` -- """ -- line = infile[cur_index] -- cur_index += 1 -- message = text % cur_index -- error = ErrorClass(message, cur_index, line) -- if self.raise_errors: -- # raise the error - parsing stops here -- raise error -- # store the error -- # reraise when parsing has finished -- self._errors.append(error) -- -- -- def _unquote(self, value): -- """Return an unquoted version of a value""" -- if (value[0] == value[-1]) and (value[0] in ('"', "'")): -- value = value[1:-1] -- return value -- -- -- def _quote(self, value, multiline=True): -- """ -- Return a safely quoted version of a value. -- -- Raise a ConfigObjError if the value cannot be safely quoted. -- If multiline is ``True`` (default) then use triple quotes -- if necessary. -- -- Don't quote values that don't need it. -- Recursively quote members of a list and return a comma joined list. -- Multiline is ``False`` for lists. -- Obey list syntax for empty and single member lists. -- -- If ``list_values=False`` then the value is only quoted if it contains -- a ``\n`` (is multiline) or '#'. -- -- If ``write_empty_values`` is set, and the value is an empty string, it -- won't be quoted. -- """ -- if multiline and self.write_empty_values and value == '': -- # Only if multiline is set, so that it is used for values not -- # keys, and not values that are part of a list -- return '' -- -- if multiline and isinstance(value, (list, tuple)): -- if not value: -- return ',' -- elif len(value) == 1: -- return self._quote(value[0], multiline=False) + ',' -- return ', '.join([self._quote(val, multiline=False) -- for val in value]) -- if not isinstance(value, StringTypes): -- if self.stringify: -- value = str(value) -- else: -- raise TypeError('Value "%s" is not a string.' % value) -- -- if not value: -- return '""' -- -- no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value -- need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value )) -- hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value) -- check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote -- -- if check_for_single: -- if not self.list_values: -- # we don't quote if ``list_values=False`` -- quot = noquot -- # for normal values either single or double quotes will do -- elif '\n' in value: -- # will only happen if multiline is off - e.g. '\n' in key -- raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) -- elif ((value[0] not in wspace_plus) and -- (value[-1] not in wspace_plus) and -- (',' not in value)): -- quot = noquot -- else: -- quot = self._get_single_quote(value) -- else: -- # if value has '\n' or "'" *and* '"', it will need triple quotes -- quot = self._get_triple_quote(value) -- -- if quot == noquot and '#' in value and self.list_values: -- quot = self._get_single_quote(value) -- -- return quot % value -- -- -- def _get_single_quote(self, value): -- if ("'" in value) and ('"' in value): -- raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) -- elif '"' in value: -- quot = squot -- else: -- quot = dquot -- return quot -- -- -- def _get_triple_quote(self, value): -- if (value.find('"""') != -1) and (value.find("'''") != -1): -- raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) -- if value.find('"""') == -1: -- quot = tdquot -- else: -- quot = tsquot -- return quot -- -- -- def _handle_value(self, value): -- """ -- Given a value string, unquote, remove comment, -- handle lists. (including empty and single member lists) -- """ -- # do we look for lists in values ? -- if not self.list_values: -- mat = self._nolistvalue.match(value) -- if mat is None: -- raise SyntaxError() -- # NOTE: we don't unquote here -- return mat.groups() -- # -- mat = self._valueexp.match(value) -- if mat is None: -- # the value is badly constructed, probably badly quoted, -- # or an invalid list -- raise SyntaxError() -- (list_values, single, empty_list, comment) = mat.groups() -- if (list_values == '') and (single is None): -- # change this if you want to accept empty values -- raise SyntaxError() -- # NOTE: note there is no error handling from here if the regex -- # is wrong: then incorrect values will slip through -- if empty_list is not None: -- # the single comma - meaning an empty list -- return ([], comment) -- if single is not None: -- # handle empty values -- if list_values and not single: -- # FIXME: the '' is a workaround because our regex now matches -- # '' at the end of a list if it has a trailing comma -- single = None -- else: -- single = single or '""' -- single = self._unquote(single) -- if list_values == '': -- # not a list value -- return (single, comment) -- the_list = self._listvalueexp.findall(list_values) -- the_list = [self._unquote(val) for val in the_list] -- if single is not None: -- the_list += [single] -- return (the_list, comment) -- -- -- def _multiline(self, value, infile, cur_index, maxline): -- """Extract the value, where we are in a multiline situation.""" -- quot = value[:3] -- newvalue = value[3:] -- single_line = self._triple_quote[quot][0] -- multi_line = self._triple_quote[quot][1] -- mat = single_line.match(value) -- if mat is not None: -- retval = list(mat.groups()) -- retval.append(cur_index) -- return retval -- elif newvalue.find(quot) != -1: -- # somehow the triple quote is missing -- raise SyntaxError() -- # -- while cur_index < maxline: -- cur_index += 1 -- newvalue += '\n' -- line = infile[cur_index] -- if line.find(quot) == -1: -- newvalue += line -- else: -- # end of multiline, process it -- break -- else: -- # we've got to the end of the config, oops... -- raise SyntaxError() -- mat = multi_line.match(line) -- if mat is None: -- # a badly formed line -- raise SyntaxError() -- (value, comment) = mat.groups() -- return (newvalue + value, comment, cur_index) -- -- -- def _handle_configspec(self, configspec): -- """Parse the configspec.""" -- # FIXME: Should we check that the configspec was created with the -- # correct settings ? (i.e. ``list_values=False``) -- if not isinstance(configspec, ConfigObj): -- try: -- configspec = ConfigObj(configspec, -- raise_errors=True, -- file_error=True, -- list_values=False) -- except ConfigObjError, e: -- # FIXME: Should these errors have a reference -- # to the already parsed ConfigObj ? -- raise ConfigspecError('Parsing configspec failed: %s' % e) -- except IOError, e: -- raise IOError('Reading configspec failed: %s' % e) -- -- self._set_configspec_value(configspec, self) -- -- -- def _set_configspec_value(self, configspec, section): -- """Used to recursively set configspec values.""" -- if '__many__' in configspec.sections: -- section.configspec['__many__'] = configspec['__many__'] -- if len(configspec.sections) > 1: -- # FIXME: can we supply any useful information here ? -- raise RepeatSectionError() -- -- if hasattr(configspec, 'initial_comment'): -- section._configspec_initial_comment = configspec.initial_comment -- section._configspec_final_comment = configspec.final_comment -- section._configspec_encoding = configspec.encoding -- section._configspec_BOM = configspec.BOM -- section._configspec_newlines = configspec.newlines -- section._configspec_indent_type = configspec.indent_type -- -- for entry in configspec.scalars: -- section._configspec_comments[entry] = configspec.comments[entry] -- section._configspec_inline_comments[entry] = configspec.inline_comments[entry] -- section.configspec[entry] = configspec[entry] -- section._order.append(entry) -- -- for entry in configspec.sections: -- if entry == '__many__': -- continue -- -- section._cs_section_comments[entry] = configspec.comments[entry] -- section._cs_section_inline_comments[entry] = configspec.inline_comments[entry] -- if not section.has_key(entry): -- section[entry] = {} -- self._set_configspec_value(configspec[entry], section[entry]) -- -- -- def _handle_repeat(self, section, configspec): -- """Dynamically assign configspec for repeated section.""" -- try: -- section_keys = configspec.sections -- scalar_keys = configspec.scalars -- except AttributeError: -- section_keys = [entry for entry in configspec -- if isinstance(configspec[entry], dict)] -- scalar_keys = [entry for entry in configspec -- if not isinstance(configspec[entry], dict)] -- -- if '__many__' in section_keys and len(section_keys) > 1: -- # FIXME: can we supply any useful information here ? -- raise RepeatSectionError() -- -- scalars = {} -- sections = {} -- for entry in scalar_keys: -- val = configspec[entry] -- scalars[entry] = val -- for entry in section_keys: -- val = configspec[entry] -- if entry == '__many__': -- scalars[entry] = val -- continue -- sections[entry] = val -- -- section.configspec = scalars -- for entry in sections: -- if not section.has_key(entry): -- section[entry] = {} -- self._handle_repeat(section[entry], sections[entry]) -- -- -- def _write_line(self, indent_string, entry, this_entry, comment): -- """Write an individual line, for the write method""" -- # NOTE: the calls to self._quote here handles non-StringType values. -- if not self.unrepr: -- val = self._decode_element(self._quote(this_entry)) -- else: -- val = repr(this_entry) -- return '%s%s%s%s%s' % (indent_string, -- self._decode_element(self._quote(entry, multiline=False)), -- self._a_to_u(' = '), -- val, -- self._decode_element(comment)) -- -- -- def _write_marker(self, indent_string, depth, entry, comment): -- """Write a section marker line""" -- return '%s%s%s%s%s' % (indent_string, -- self._a_to_u('[' * depth), -- self._quote(self._decode_element(entry), multiline=False), -- self._a_to_u(']' * depth), -- self._decode_element(comment)) -- -- -- def _handle_comment(self, comment): -- """Deal with a comment.""" -- if not comment: -- return '' -- start = self.indent_type -- if not comment.startswith('#'): -- start += self._a_to_u(' # ') -- return (start + comment) -- -- -- # Public methods -- -- def write(self, outfile=None, section=None): -- """ -- Write the current ConfigObj as a file -- -- tekNico: FIXME: use StringIO instead of real files -- -- >>> filename = a.filename -- >>> a.filename = 'test.ini' -- >>> a.write() -- >>> a.filename = filename -- >>> a == ConfigObj('test.ini', raise_errors=True) -- 1 -- """ -- if self.indent_type is None: -- # this can be true if initialised from a dictionary -- self.indent_type = DEFAULT_INDENT_TYPE -- -- out = [] -- cs = self._a_to_u('#') -- csp = self._a_to_u('# ') -- if section is None: -- int_val = self.interpolation -- self.interpolation = False -- section = self -- for line in self.initial_comment: -- line = self._decode_element(line) -- stripped_line = line.strip() -- if stripped_line and not stripped_line.startswith(cs): -- line = csp + line -- out.append(line) -- -- indent_string = self.indent_type * section.depth -- for entry in (section.scalars + section.sections): -- if entry in section.defaults: -- # don't write out default values -- continue -- for comment_line in section.comments[entry]: -- comment_line = self._decode_element(comment_line.lstrip()) -- if comment_line and not comment_line.startswith(cs): -- comment_line = csp + comment_line -- out.append(indent_string + comment_line) -- this_entry = section[entry] -- comment = self._handle_comment(section.inline_comments[entry]) -- -- if isinstance(this_entry, dict): -- # a section -- out.append(self._write_marker( -- indent_string, -- this_entry.depth, -- entry, -- comment)) -- out.extend(self.write(section=this_entry)) -- else: -- out.append(self._write_line( -- indent_string, -- entry, -- this_entry, -- comment)) -- -- if section is self: -- for line in self.final_comment: -- line = self._decode_element(line) -- stripped_line = line.strip() -- if stripped_line and not stripped_line.startswith(cs): -- line = csp + line -- out.append(line) -- self.interpolation = int_val -- -- if section is not self: -- return out -- -- if (self.filename is None) and (outfile is None): -- # output a list of lines -- # might need to encode -- # NOTE: This will *screw* UTF16, each line will start with the BOM -- if self.encoding: -- out = [l.encode(self.encoding) for l in out] -- if (self.BOM and ((self.encoding is None) or -- (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))): -- # Add the UTF8 BOM -- if not out: -- out.append('') -- out[0] = BOM_UTF8 + out[0] -- return out -- -- # Turn the list to a string, joined with correct newlines -- newline = self.newlines or os.linesep -- output = self._a_to_u(newline).join(out) -- if self.encoding: -- output = output.encode(self.encoding) -- if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)): -- # Add the UTF8 BOM -- output = BOM_UTF8 + output -- -- if not output.endswith(newline): -- output += newline -- if outfile is not None: -- outfile.write(output) -- else: -- h = open(self.filename, 'wb') -- h.write(output) -- h.close() -- -- -- def validate(self, validator, preserve_errors=False, copy=False, -- section=None): -- """ -- Test the ConfigObj against a configspec. -- -- It uses the ``validator`` object from *validate.py*. -- -- To run ``validate`` on the current ConfigObj, call: :: -- -- test = config.validate(validator) -- -- (Normally having previously passed in the configspec when the ConfigObj -- was created - you can dynamically assign a dictionary of checks to the -- ``configspec`` attribute of a section though). -- -- It returns ``True`` if everything passes, or a dictionary of -- pass/fails (True/False). If every member of a subsection passes, it -- will just have the value ``True``. (It also returns ``False`` if all -- members fail). -- -- In addition, it converts the values from strings to their native -- types if their checks pass (and ``stringify`` is set). -- -- If ``preserve_errors`` is ``True`` (``False`` is default) then instead -- of a marking a fail with a ``False``, it will preserve the actual -- exception object. This can contain info about the reason for failure. -- For example the ``VdtValueTooSmallError`` indicates that the value -- supplied was too small. If a value (or section) is missing it will -- still be marked as ``False``. -- -- You must have the validate module to use ``preserve_errors=True``. -- -- You can then use the ``flatten_errors`` function to turn your nested -- results dictionary into a flattened list of failures - useful for -- displaying meaningful error messages. -- """ -- if section is None: -- if self.configspec is None: -- raise ValueError('No configspec supplied.') -- if preserve_errors: -- # We do this once to remove a top level dependency on the validate module -- # Which makes importing configobj faster -- from validate import VdtMissingValue -- self._vdtMissingValue = VdtMissingValue -- section = self -- # -- spec_section = section.configspec -- if copy and hasattr(section, '_configspec_initial_comment'): -- section.initial_comment = section._configspec_initial_comment -- section.final_comment = section._configspec_final_comment -- section.encoding = section._configspec_encoding -- section.BOM = section._configspec_BOM -- section.newlines = section._configspec_newlines -- section.indent_type = section._configspec_indent_type -- -- if '__many__' in section.configspec: -- many = spec_section['__many__'] -- # dynamically assign the configspecs -- # for the sections below -- for entry in section.sections: -- self._handle_repeat(section[entry], many) -- # -- out = {} -- ret_true = True -- ret_false = True -- order = [k for k in section._order if k in spec_section] -- order += [k for k in spec_section if k not in order] -- for entry in order: -- if entry == '__many__': -- continue -- if (not entry in section.scalars) or (entry in section.defaults): -- # missing entries -- # or entries from defaults -- missing = True -- val = None -- if copy and not entry in section.scalars: -- # copy comments -- section.comments[entry] = ( -- section._configspec_comments.get(entry, [])) -- section.inline_comments[entry] = ( -- section._configspec_inline_comments.get(entry, '')) -- # -- else: -- missing = False -- val = section[entry] -- try: -- check = validator.check(spec_section[entry], -- val, -- missing=missing -- ) -- except validator.baseErrorClass, e: -- if not preserve_errors or isinstance(e, self._vdtMissingValue): -- out[entry] = False -- else: -- # preserve the error -- out[entry] = e -- ret_false = False -- ret_true = False -- else: -- try: -- section.default_values.pop(entry, None) -- except AttributeError: -- # For Python 2.2 compatibility -- try: -- del section.default_values[entry] -- except KeyError: -- pass -- -- if hasattr(validator, 'get_default_value'): -- try: -- section.default_values[entry] = validator.get_default_value(spec_section[entry]) -- except KeyError: -- # No default -- pass -- -- ret_false = False -- out[entry] = True -- if self.stringify or missing: -- # if we are doing type conversion -- # or the value is a supplied default -- if not self.stringify: -- if isinstance(check, (list, tuple)): -- # preserve lists -- check = [self._str(item) for item in check] -- elif missing and check is None: -- # convert the None from a default to a '' -- check = '' -- else: -- check = self._str(check) -- if (check != val) or missing: -- section[entry] = check -- if not copy and missing and entry not in section.defaults: -- section.defaults.append(entry) -- # Missing sections will have been created as empty ones when the -- # configspec was read. -- for entry in section.sections: -- # FIXME: this means DEFAULT is not copied in copy mode -- if section is self and entry == 'DEFAULT': -- continue -- if copy: -- section.comments[entry] = section._cs_section_comments[entry] -- section.inline_comments[entry] = ( -- section._cs_section_inline_comments[entry]) -- check = self.validate(validator, preserve_errors=preserve_errors, -- copy=copy, section=section[entry]) -- out[entry] = check -- if check == False: -- ret_true = False -- elif check == True: -- ret_false = False -- else: -- ret_true = False -- ret_false = False -- # -- if ret_true: -- return True -- elif ret_false: -- return False -- return out -- -- -- def reset(self): -- """Clear ConfigObj instance and restore to 'freshly created' state.""" -- self.clear() -- self._initialise() -- # FIXME: Should be done by '_initialise', but ConfigObj constructor (and reload) -- # requires an empty dictionary -- self.configspec = None -- # Just to be sure ;-) -- self._original_configspec = None -- -- -- def reload(self): -- """ -- Reload a ConfigObj from file. -- -- This method raises a ``ReloadError`` if the ConfigObj doesn't have -- a filename attribute pointing to a file. -- """ -- if not isinstance(self.filename, StringTypes): -- raise ReloadError() -- -- filename = self.filename -- current_options = {} -- for entry in OPTION_DEFAULTS: -- if entry == 'configspec': -- continue -- current_options[entry] = getattr(self, entry) -- -- configspec = self._original_configspec -- current_options['configspec'] = configspec -- -- self.clear() -- self._initialise(current_options) -- self._load(filename, configspec) -- -- -- --class SimpleVal(object): -- """ -- A simple validator. -- Can be used to check that all members expected are present. -- -- To use it, provide a configspec with all your members in (the value given -- will be ignored). Pass an instance of ``SimpleVal`` to the ``validate`` -- method of your ``ConfigObj``. ``validate`` will return ``True`` if all -- members are present, or a dictionary with True/False meaning -- present/missing. (Whole missing sections will be replaced with ``False``) -- """ -- -- def __init__(self): -- self.baseErrorClass = ConfigObjError -- -- def check(self, check, member, missing=False): -- """A dummy check method, always returns the value unchanged.""" -- if missing: -- raise self.baseErrorClass() -- return member -- -- --# Check / processing functions for options --def flatten_errors(cfg, res, levels=None, results=None): -- """ -- An example function that will turn a nested dictionary of results -- (as returned by ``ConfigObj.validate``) into a flat list. -- -- ``cfg`` is the ConfigObj instance being checked, ``res`` is the results -- dictionary returned by ``validate``. -- -- (This is a recursive function, so you shouldn't use the ``levels`` or -- ``results`` arguments - they are used by the function. -- -- Returns a list of keys that failed. Each member of the list is a tuple : -- :: -- -- ([list of sections...], key, result) -- -- If ``validate`` was called with ``preserve_errors=False`` (the default) -- then ``result`` will always be ``False``. -- -- *list of sections* is a flattened list of sections that the key was found -- in. -- -- If the section was missing then key will be ``None``. -- -- If the value (or section) was missing then ``result`` will be ``False``. -- -- If ``validate`` was called with ``preserve_errors=True`` and a value -- was present, but failed the check, then ``result`` will be the exception -- object returned. You can use this as a string that describes the failure. -- -- For example *The value "3" is of the wrong type*. -- -- >>> import validate -- >>> vtor = validate.Validator() -- >>> my_ini = ''' -- ... option1 = True -- ... [section1] -- ... option1 = True -- ... [section2] -- ... another_option = Probably -- ... [section3] -- ... another_option = True -- ... [[section3b]] -- ... value = 3 -- ... value2 = a -- ... value3 = 11 -- ... ''' -- >>> my_cfg = ''' -- ... option1 = boolean() -- ... option2 = boolean() -- ... option3 = boolean(default=Bad_value) -- ... [section1] -- ... option1 = boolean() -- ... option2 = boolean() -- ... option3 = boolean(default=Bad_value) -- ... [section2] -- ... another_option = boolean() -- ... [section3] -- ... another_option = boolean() -- ... [[section3b]] -- ... value = integer -- ... value2 = integer -- ... value3 = integer(0, 10) -- ... [[[section3b-sub]]] -- ... value = string -- ... [section4] -- ... another_option = boolean() -- ... ''' -- >>> cs = my_cfg.split('\\n') -- >>> ini = my_ini.split('\\n') -- >>> cfg = ConfigObj(ini, configspec=cs) -- >>> res = cfg.validate(vtor, preserve_errors=True) -- >>> errors = [] -- >>> for entry in flatten_errors(cfg, res): -- ... section_list, key, error = entry -- ... section_list.insert(0, '[root]') -- ... if key is not None: -- ... section_list.append(key) -- ... else: -- ... section_list.append('[missing]') -- ... section_string = ', '.join(section_list) -- ... errors.append((section_string, ' = ', error)) -- >>> errors.sort() -- >>> for entry in errors: -- ... print entry[0], entry[1], (entry[2] or 0) -- [root], option2 = 0 -- [root], option3 = the value "Bad_value" is of the wrong type. -- [root], section1, option2 = 0 -- [root], section1, option3 = the value "Bad_value" is of the wrong type. -- [root], section2, another_option = the value "Probably" is of the wrong type. -- [root], section3, section3b, section3b-sub, [missing] = 0 -- [root], section3, section3b, value2 = the value "a" is of the wrong type. -- [root], section3, section3b, value3 = the value "11" is too big. -- [root], section4, [missing] = 0 -- """ -- if levels is None: -- # first time called -- levels = [] -- results = [] -- if res is True: -- return results -- if res is False: -- results.append((levels[:], None, False)) -- if levels: -- levels.pop() -- return results -- for (key, val) in res.items(): -- if val == True: -- continue -- if isinstance(cfg.get(key), dict): -- # Go down one level -- levels.append(key) -- flatten_errors(cfg[key], val, levels, results) -- continue -- results.append((levels[:], key, val)) -- # -- # Go up one level -- if levels: -- levels.pop() -- # -- return results -- -- --"""*A programming language is a medium of expression.* - Paul Graham""" -Index: ipython-0.10/IPython/external/guid/_guid.py -=================================================================== ---- /dev/null -+++ ipython-0.10/IPython/external/guid/_guid.py -@@ -0,0 +1,170 @@ -+#!/usr/bin/env python -+# encoding: utf-8 -+ -+# GUID.py -+# Version 2.6 -+# -+# Copyright (c) 2006 Conan C. Albrecht -+# -+# Permission is hereby granted, free of charge, to any person obtaining a copy -+# of this software and associated documentation files (the "Software"), to deal -+# in the Software without restriction, including without limitation the rights -+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -+# copies of the Software, and to permit persons to whom the Software is furnished -+# to do so, subject to the following conditions: -+# -+# The above copyright notice and this permission notice shall be included in all -+# copies or substantial portions of the Software. -+# -+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -+# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -+# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -+# FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -+# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -+# DEALINGS IN THE SOFTWARE. -+ -+ -+ -+################################################################################################## -+### A globally-unique identifier made up of time and ip and 8 digits for a counter: -+### each GUID is 40 characters wide -+### -+### A globally unique identifier that combines ip, time, and a counter. Since the -+### time is listed first, you can sort records by guid. You can also extract the time -+### and ip if needed. -+### -+### Since the counter has eight hex characters, you can create up to -+### 0xffffffff (4294967295) GUIDs every millisecond. If your processor -+### is somehow fast enough to create more than that in a millisecond (looking -+### toward the future, of course), the function will wait until the next -+### millisecond to return. -+### -+### GUIDs make wonderful database keys. They require no access to the -+### database (to get the max index number), they are extremely unique, and they sort -+### automatically by time. GUIDs prevent key clashes when merging -+### two databases together, combining data, or generating keys in distributed -+### systems. -+### -+### There is an Internet Draft for UUIDs, but this module does not implement it. -+### If the draft catches on, perhaps I'll conform the module to it. -+### -+ -+ -+# Changelog -+# Sometime, 1997 Created the Java version of GUID -+# Went through many versions in Java -+# Sometime, 2002 Created the Python version of GUID, mirroring the Java version -+# November 24, 2003 Changed Python version to be more pythonic, took out object and made just a module -+# December 2, 2003 Fixed duplicating GUIDs. Sometimes they duplicate if multiples are created -+# in the same millisecond (it checks the last 100 GUIDs now and has a larger random part) -+# December 9, 2003 Fixed MAX_RANDOM, which was going over sys.maxint -+# June 12, 2004 Allowed a custom IP address to be sent in rather than always using the -+# local IP address. -+# November 4, 2005 Changed the random part to a counter variable. Now GUIDs are totally -+# unique and more efficient, as long as they are created by only -+# on runtime on a given machine. The counter part is after the time -+# part so it sorts correctly. -+# November 8, 2005 The counter variable now starts at a random long now and cycles -+# around. This is in case two guids are created on the same -+# machine at the same millisecond (by different processes). Even though -+# it is possible the GUID can be created, this makes it highly unlikely -+# since the counter will likely be different. -+# November 11, 2005 Fixed a bug in the new IP getting algorithm. Also, use IPv6 range -+# for IP when we make it up (when it's no accessible) -+# November 21, 2005 Added better IP-finding code. It finds IP address better now. -+# January 5, 2006 Fixed a small bug caused in old versions of python (random module use) -+ -+import math -+import socket -+import random -+import sys -+import time -+import threading -+ -+ -+ -+############################# -+### global module variables -+ -+#Makes a hex IP from a decimal dot-separated ip (eg: 127.0.0.1) -+make_hexip = lambda ip: ''.join(["%04x" % long(i) for i in ip.split('.')]) # leave space for ip v6 (65K in each sub) -+ -+MAX_COUNTER = 0xfffffffe -+counter = 0L -+firstcounter = MAX_COUNTER -+lasttime = 0 -+ip = '' -+lock = threading.RLock() -+try: # only need to get the IP addresss once -+ ip = socket.getaddrinfo(socket.gethostname(),0)[-1][-1][0] -+ hexip = make_hexip(ip) -+except: # if we don't have an ip, default to someting in the 10.x.x.x private range -+ ip = '10' -+ rand = random.Random() -+ for i in range(3): -+ ip += '.' + str(rand.randrange(1, 0xffff)) # might as well use IPv6 range if we're making it up -+ hexip = make_hexip(ip) -+ -+ -+################################# -+### Public module functions -+ -+def generate(ip=None): -+ '''Generates a new guid. A guid is unique in space and time because it combines -+ the machine IP with the current time in milliseconds. Be careful about sending in -+ a specified IP address because the ip makes it unique in space. You could send in -+ the same IP address that is created on another machine. -+ ''' -+ global counter, firstcounter, lasttime -+ lock.acquire() # can't generate two guids at the same time -+ try: -+ parts = [] -+ -+ # do we need to wait for the next millisecond (are we out of counters?) -+ now = long(time.time() * 1000) -+ while lasttime == now and counter == firstcounter: -+ time.sleep(.01) -+ now = long(time.time() * 1000) -+ -+ # time part -+ parts.append("%016x" % now) -+ -+ # counter part -+ if lasttime != now: # time to start counter over since we have a different millisecond -+ firstcounter = long(random.uniform(1, MAX_COUNTER)) # start at random position -+ counter = firstcounter -+ counter += 1 -+ if counter > MAX_COUNTER: -+ counter = 0 -+ lasttime = now -+ parts.append("%08x" % (counter)) -+ -+ # ip part -+ parts.append(hexip) -+ -+ # put them all together -+ return ''.join(parts) -+ finally: -+ lock.release() -+ -+ -+def extract_time(guid): -+ '''Extracts the time portion out of the guid and returns the -+ number of seconds since the epoch as a float''' -+ return float(long(guid[0:16], 16)) / 1000.0 -+ -+ -+def extract_counter(guid): -+ '''Extracts the counter from the guid (returns the bits in decimal)''' -+ return int(guid[16:24], 16) -+ -+ -+def extract_ip(guid): -+ '''Extracts the ip portion out of the guid and returns it -+ as a string like 10.10.10.10''' -+ # there's probably a more elegant way to do this -+ thisip = [] -+ for index in range(24, 40, 4): -+ thisip.append(str(int(guid[index: index + 4], 16))) -+ return '.'.join(thisip) -+ Index: ipython-0.10/IPython/external/guid/__init__.py =================================================================== --- /dev/null @@ -9661,181 +32,6 @@ Index: ipython-0.10/IPython/external/guid/__init__.py + from guid import * +except ImportError: + from _guid import * -Index: ipython-0.10/IPython/external/guid.py -=================================================================== ---- ipython-0.10.orig/IPython/external/guid.py -+++ /dev/null -@@ -1,170 +0,0 @@ --#!/usr/bin/env python --# encoding: utf-8 -- --# GUID.py --# Version 2.6 --# --# Copyright (c) 2006 Conan C. Albrecht --# --# Permission is hereby granted, free of charge, to any person obtaining a copy --# of this software and associated documentation files (the "Software"), to deal --# in the Software without restriction, including without limitation the rights --# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell --# copies of the Software, and to permit persons to whom the Software is furnished --# to do so, subject to the following conditions: --# --# The above copyright notice and this permission notice shall be included in all --# copies or substantial portions of the Software. --# --# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, --# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR --# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE --# FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR --# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER --# DEALINGS IN THE SOFTWARE. -- -- -- --################################################################################################## --### A globally-unique identifier made up of time and ip and 8 digits for a counter: --### each GUID is 40 characters wide --### --### A globally unique identifier that combines ip, time, and a counter. Since the --### time is listed first, you can sort records by guid. You can also extract the time --### and ip if needed. --### --### Since the counter has eight hex characters, you can create up to --### 0xffffffff (4294967295) GUIDs every millisecond. If your processor --### is somehow fast enough to create more than that in a millisecond (looking --### toward the future, of course), the function will wait until the next --### millisecond to return. --### --### GUIDs make wonderful database keys. They require no access to the --### database (to get the max index number), they are extremely unique, and they sort --### automatically by time. GUIDs prevent key clashes when merging --### two databases together, combining data, or generating keys in distributed --### systems. --### --### There is an Internet Draft for UUIDs, but this module does not implement it. --### If the draft catches on, perhaps I'll conform the module to it. --### -- -- --# Changelog --# Sometime, 1997 Created the Java version of GUID --# Went through many versions in Java --# Sometime, 2002 Created the Python version of GUID, mirroring the Java version --# November 24, 2003 Changed Python version to be more pythonic, took out object and made just a module --# December 2, 2003 Fixed duplicating GUIDs. Sometimes they duplicate if multiples are created --# in the same millisecond (it checks the last 100 GUIDs now and has a larger random part) --# December 9, 2003 Fixed MAX_RANDOM, which was going over sys.maxint --# June 12, 2004 Allowed a custom IP address to be sent in rather than always using the --# local IP address. --# November 4, 2005 Changed the random part to a counter variable. Now GUIDs are totally --# unique and more efficient, as long as they are created by only --# on runtime on a given machine. The counter part is after the time --# part so it sorts correctly. --# November 8, 2005 The counter variable now starts at a random long now and cycles --# around. This is in case two guids are created on the same --# machine at the same millisecond (by different processes). Even though --# it is possible the GUID can be created, this makes it highly unlikely --# since the counter will likely be different. --# November 11, 2005 Fixed a bug in the new IP getting algorithm. Also, use IPv6 range --# for IP when we make it up (when it's no accessible) --# November 21, 2005 Added better IP-finding code. It finds IP address better now. --# January 5, 2006 Fixed a small bug caused in old versions of python (random module use) -- --import math --import socket --import random --import sys --import time --import threading -- -- -- --############################# --### global module variables -- --#Makes a hex IP from a decimal dot-separated ip (eg: 127.0.0.1) --make_hexip = lambda ip: ''.join(["%04x" % long(i) for i in ip.split('.')]) # leave space for ip v6 (65K in each sub) -- --MAX_COUNTER = 0xfffffffe --counter = 0L --firstcounter = MAX_COUNTER --lasttime = 0 --ip = '' --lock = threading.RLock() --try: # only need to get the IP addresss once -- ip = socket.getaddrinfo(socket.gethostname(),0)[-1][-1][0] -- hexip = make_hexip(ip) --except: # if we don't have an ip, default to someting in the 10.x.x.x private range -- ip = '10' -- rand = random.Random() -- for i in range(3): -- ip += '.' + str(rand.randrange(1, 0xffff)) # might as well use IPv6 range if we're making it up -- hexip = make_hexip(ip) -- -- --################################# --### Public module functions -- --def generate(ip=None): -- '''Generates a new guid. A guid is unique in space and time because it combines -- the machine IP with the current time in milliseconds. Be careful about sending in -- a specified IP address because the ip makes it unique in space. You could send in -- the same IP address that is created on another machine. -- ''' -- global counter, firstcounter, lasttime -- lock.acquire() # can't generate two guids at the same time -- try: -- parts = [] -- -- # do we need to wait for the next millisecond (are we out of counters?) -- now = long(time.time() * 1000) -- while lasttime == now and counter == firstcounter: -- time.sleep(.01) -- now = long(time.time() * 1000) -- -- # time part -- parts.append("%016x" % now) -- -- # counter part -- if lasttime != now: # time to start counter over since we have a different millisecond -- firstcounter = long(random.uniform(1, MAX_COUNTER)) # start at random position -- counter = firstcounter -- counter += 1 -- if counter > MAX_COUNTER: -- counter = 0 -- lasttime = now -- parts.append("%08x" % (counter)) -- -- # ip part -- parts.append(hexip) -- -- # put them all together -- return ''.join(parts) -- finally: -- lock.release() -- -- --def extract_time(guid): -- '''Extracts the time portion out of the guid and returns the -- number of seconds since the epoch as a float''' -- return float(long(guid[0:16], 16)) / 1000.0 -- -- --def extract_counter(guid): -- '''Extracts the counter from the guid (returns the bits in decimal)''' -- return int(guid[16:24], 16) -- -- --def extract_ip(guid): -- '''Extracts the ip portion out of the guid and returns it -- as a string like 10.10.10.10''' -- # there's probably a more elegant way to do this -- thisip = [] -- for index in range(24, 40, 4): -- thisip.append(str(int(guid[index: index + 4], 16))) -- return '.'.join(thisip) -- Index: ipython-0.10/IPython/external/Itpl/__init__.py =================================================================== --- /dev/null @@ -9845,568 +41,6 @@ Index: ipython-0.10/IPython/external/Itpl/__init__.py + from Itpl import * +except ImportError: + from _Itpl import * -Index: ipython-0.10/IPython/external/Itpl/_Itpl.py -=================================================================== ---- /dev/null -+++ ipython-0.10/IPython/external/Itpl/_Itpl.py -@@ -0,0 +1,276 @@ -+# -*- coding: utf-8 -*- -+"""String interpolation for Python (by Ka-Ping Yee, 14 Feb 2000). -+ -+This module lets you quickly and conveniently interpolate values into -+strings (in the flavour of Perl or Tcl, but with less extraneous -+punctuation). You get a bit more power than in the other languages, -+because this module allows subscripting, slicing, function calls, -+attribute lookup, or arbitrary expressions. Variables and expressions -+are evaluated in the namespace of the caller. -+ -+The itpl() function returns the result of interpolating a string, and -+printpl() prints out an interpolated string. Here are some examples: -+ -+ from Itpl import printpl -+ printpl("Here is a $string.") -+ printpl("Here is a $module.member.") -+ printpl("Here is an $object.member.") -+ printpl("Here is a $functioncall(with, arguments).") -+ printpl("Here is an ${arbitrary + expression}.") -+ printpl("Here is an $array[3] member.") -+ printpl("Here is a $dictionary['member'].") -+ -+The filter() function filters a file object so that output through it -+is interpolated. This lets you produce the illusion that Python knows -+how to do interpolation: -+ -+ import Itpl -+ sys.stdout = Itpl.filter() -+ f = "fancy" -+ print "Is this not $f?" -+ print "Standard output has been replaced with a $sys.stdout object." -+ sys.stdout = Itpl.unfilter() -+ print "Okay, back $to $normal." -+ -+Under the hood, the Itpl class represents a string that knows how to -+interpolate values. An instance of the class parses the string once -+upon initialization; the evaluation and substitution can then be done -+each time the instance is evaluated with str(instance). For example: -+ -+ from Itpl import Itpl -+ s = Itpl("Here is $foo.") -+ foo = 5 -+ print str(s) -+ foo = "bar" -+ print str(s) -+""" -+ -+#***************************************************************************** -+# -+# Copyright (c) 2001 Ka-Ping Yee -+# -+# -+# Published under the terms of the MIT license, hereby reproduced: -+# -+# Permission is hereby granted, free of charge, to any person obtaining a copy -+# of this software and associated documentation files (the "Software"), to -+# deal in the Software without restriction, including without limitation the -+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -+# sell copies of the Software, and to permit persons to whom the Software is -+# furnished to do so, subject to the following conditions: -+# -+# The above copyright notice and this permission notice shall be included in -+# all copies or substantial portions of the Software. -+# -+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -+# IN THE SOFTWARE. -+# -+#***************************************************************************** -+ -+__author__ = 'Ka-Ping Yee ' -+__license__ = 'MIT' -+ -+import string -+import sys -+from tokenize import tokenprog -+from types import StringType -+ -+class ItplError(ValueError): -+ def __init__(self, text, pos): -+ self.text = text -+ self.pos = pos -+ def __str__(self): -+ return "unfinished expression in %s at char %d" % ( -+ repr(self.text), self.pos) -+ -+def matchorfail(text, pos): -+ match = tokenprog.match(text, pos) -+ if match is None: -+ raise ItplError(text, pos) -+ return match, match.end() -+ -+class Itpl: -+ """Class representing a string with interpolation abilities. -+ -+ Upon creation, an instance works out what parts of the format -+ string are literal and what parts need to be evaluated. The -+ evaluation and substitution happens in the namespace of the -+ caller when str(instance) is called.""" -+ -+ def __init__(self, format,codec='utf_8',encoding_errors='backslashreplace'): -+ """The single mandatory argument to this constructor is a format -+ string. -+ -+ The format string is parsed according to the following rules: -+ -+ 1. A dollar sign and a name, possibly followed by any of: -+ - an open-paren, and anything up to the matching paren -+ - an open-bracket, and anything up to the matching bracket -+ - a period and a name -+ any number of times, is evaluated as a Python expression. -+ -+ 2. A dollar sign immediately followed by an open-brace, and -+ anything up to the matching close-brace, is evaluated as -+ a Python expression. -+ -+ 3. Outside of the expressions described in the above two rules, -+ two dollar signs in a row give you one literal dollar sign. -+ -+ Optional arguments: -+ -+ - codec('utf_8'): a string containing the name of a valid Python -+ codec. -+ -+ - encoding_errors('backslashreplace'): a string with a valid error handling -+ policy. See the codecs module documentation for details. -+ -+ These are used to encode the format string if a call to str() fails on -+ the expanded result.""" -+ -+ if not isinstance(format,basestring): -+ raise TypeError, "needs string initializer" -+ self.format = format -+ self.codec = codec -+ self.encoding_errors = encoding_errors -+ -+ namechars = "abcdefghijklmnopqrstuvwxyz" \ -+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; -+ chunks = [] -+ pos = 0 -+ -+ while 1: -+ dollar = string.find(format, "$", pos) -+ if dollar < 0: break -+ nextchar = format[dollar+1] -+ -+ if nextchar == "{": -+ chunks.append((0, format[pos:dollar])) -+ pos, level = dollar+2, 1 -+ while level: -+ match, pos = matchorfail(format, pos) -+ tstart, tend = match.regs[3] -+ token = format[tstart:tend] -+ if token == "{": level = level+1 -+ elif token == "}": level = level-1 -+ chunks.append((1, format[dollar+2:pos-1])) -+ -+ elif nextchar in namechars: -+ chunks.append((0, format[pos:dollar])) -+ match, pos = matchorfail(format, dollar+1) -+ while pos < len(format): -+ if format[pos] == "." and \ -+ pos+1 < len(format) and format[pos+1] in namechars: -+ match, pos = matchorfail(format, pos+1) -+ elif format[pos] in "([": -+ pos, level = pos+1, 1 -+ while level: -+ match, pos = matchorfail(format, pos) -+ tstart, tend = match.regs[3] -+ token = format[tstart:tend] -+ if token[0] in "([": level = level+1 -+ elif token[0] in ")]": level = level-1 -+ else: break -+ chunks.append((1, format[dollar+1:pos])) -+ -+ else: -+ chunks.append((0, format[pos:dollar+1])) -+ pos = dollar + 1 + (nextchar == "$") -+ -+ if pos < len(format): chunks.append((0, format[pos:])) -+ self.chunks = chunks -+ -+ def __repr__(self): -+ return "" % repr(self.format) -+ -+ def _str(self,glob,loc): -+ """Evaluate to a string in the given globals/locals. -+ -+ The final output is built by calling str(), but if this fails, the -+ result is encoded with the instance's codec and error handling policy, -+ via a call to out.encode(self.codec,self.encoding_errors)""" -+ result = [] -+ app = result.append -+ for live, chunk in self.chunks: -+ if live: app(str(eval(chunk,glob,loc))) -+ else: app(chunk) -+ out = ''.join(result) -+ try: -+ return str(out) -+ except UnicodeError: -+ return out.encode(self.codec,self.encoding_errors) -+ -+ def __str__(self): -+ """Evaluate and substitute the appropriate parts of the string.""" -+ -+ # We need to skip enough frames to get to the actual caller outside of -+ # Itpl. -+ frame = sys._getframe(1) -+ while frame.f_globals["__name__"] == __name__: frame = frame.f_back -+ loc, glob = frame.f_locals, frame.f_globals -+ -+ return self._str(glob,loc) -+ -+class ItplNS(Itpl): -+ """Class representing a string with interpolation abilities. -+ -+ This inherits from Itpl, but at creation time a namespace is provided -+ where the evaluation will occur. The interpolation becomes a bit more -+ efficient, as no traceback needs to be extracte. It also allows the -+ caller to supply a different namespace for the interpolation to occur than -+ its own.""" -+ -+ def __init__(self, format,globals,locals=None, -+ codec='utf_8',encoding_errors='backslashreplace'): -+ """ItplNS(format,globals[,locals]) -> interpolating string instance. -+ -+ This constructor, besides a format string, takes a globals dictionary -+ and optionally a locals (which defaults to globals if not provided). -+ -+ For further details, see the Itpl constructor.""" -+ -+ if locals is None: -+ locals = globals -+ self.globals = globals -+ self.locals = locals -+ Itpl.__init__(self,format,codec,encoding_errors) -+ -+ def __str__(self): -+ """Evaluate and substitute the appropriate parts of the string.""" -+ return self._str(self.globals,self.locals) -+ -+ def __repr__(self): -+ return "" % repr(self.format) -+ -+# utilities for fast printing -+def itpl(text): return str(Itpl(text)) -+def printpl(text): print itpl(text) -+# versions with namespace -+def itplns(text,globals,locals=None): return str(ItplNS(text,globals,locals)) -+def printplns(text,globals,locals=None): print itplns(text,globals,locals) -+ -+class ItplFile: -+ """A file object that filters each write() through an interpolator.""" -+ def __init__(self, file): self.file = file -+ def __repr__(self): return "" -+ def __getattr__(self, attr): return getattr(self.file, attr) -+ def write(self, text): self.file.write(str(Itpl(text))) -+ -+def filter(file=sys.stdout): -+ """Return an ItplFile that filters writes to the given file object. -+ -+ 'file = filter(file)' replaces 'file' with a filtered object that -+ has a write() method. When called with no argument, this creates -+ a filter to sys.stdout.""" -+ return ItplFile(file) -+ -+def unfilter(ifile=None): -+ """Return the original file that corresponds to the given ItplFile. -+ -+ 'file = unfilter(file)' undoes the effect of 'file = filter(file)'. -+ 'sys.stdout = unfilter()' undoes the effect of 'sys.stdout = filter()'.""" -+ return ifile and ifile.file or sys.stdout.file -Index: ipython-0.10/IPython/external/Itpl.py -=================================================================== ---- ipython-0.10.orig/IPython/external/Itpl.py -+++ /dev/null -@@ -1,276 +0,0 @@ --# -*- coding: utf-8 -*- --"""String interpolation for Python (by Ka-Ping Yee, 14 Feb 2000). -- --This module lets you quickly and conveniently interpolate values into --strings (in the flavour of Perl or Tcl, but with less extraneous --punctuation). You get a bit more power than in the other languages, --because this module allows subscripting, slicing, function calls, --attribute lookup, or arbitrary expressions. Variables and expressions --are evaluated in the namespace of the caller. -- --The itpl() function returns the result of interpolating a string, and --printpl() prints out an interpolated string. Here are some examples: -- -- from Itpl import printpl -- printpl("Here is a $string.") -- printpl("Here is a $module.member.") -- printpl("Here is an $object.member.") -- printpl("Here is a $functioncall(with, arguments).") -- printpl("Here is an ${arbitrary + expression}.") -- printpl("Here is an $array[3] member.") -- printpl("Here is a $dictionary['member'].") -- --The filter() function filters a file object so that output through it --is interpolated. This lets you produce the illusion that Python knows --how to do interpolation: -- -- import Itpl -- sys.stdout = Itpl.filter() -- f = "fancy" -- print "Is this not $f?" -- print "Standard output has been replaced with a $sys.stdout object." -- sys.stdout = Itpl.unfilter() -- print "Okay, back $to $normal." -- --Under the hood, the Itpl class represents a string that knows how to --interpolate values. An instance of the class parses the string once --upon initialization; the evaluation and substitution can then be done --each time the instance is evaluated with str(instance). For example: -- -- from Itpl import Itpl -- s = Itpl("Here is $foo.") -- foo = 5 -- print str(s) -- foo = "bar" -- print str(s) --""" -- --#***************************************************************************** --# --# Copyright (c) 2001 Ka-Ping Yee --# --# --# Published under the terms of the MIT license, hereby reproduced: --# --# Permission is hereby granted, free of charge, to any person obtaining a copy --# of this software and associated documentation files (the "Software"), to --# deal in the Software without restriction, including without limitation the --# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or --# sell copies of the Software, and to permit persons to whom the Software is --# furnished to do so, subject to the following conditions: --# --# The above copyright notice and this permission notice shall be included in --# all copies or substantial portions of the Software. --# --# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR --# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, --# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE --# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER --# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING --# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS --# IN THE SOFTWARE. --# --#***************************************************************************** -- --__author__ = 'Ka-Ping Yee ' --__license__ = 'MIT' -- --import string --import sys --from tokenize import tokenprog --from types import StringType -- --class ItplError(ValueError): -- def __init__(self, text, pos): -- self.text = text -- self.pos = pos -- def __str__(self): -- return "unfinished expression in %s at char %d" % ( -- repr(self.text), self.pos) -- --def matchorfail(text, pos): -- match = tokenprog.match(text, pos) -- if match is None: -- raise ItplError(text, pos) -- return match, match.end() -- --class Itpl: -- """Class representing a string with interpolation abilities. -- -- Upon creation, an instance works out what parts of the format -- string are literal and what parts need to be evaluated. The -- evaluation and substitution happens in the namespace of the -- caller when str(instance) is called.""" -- -- def __init__(self, format,codec='utf_8',encoding_errors='backslashreplace'): -- """The single mandatory argument to this constructor is a format -- string. -- -- The format string is parsed according to the following rules: -- -- 1. A dollar sign and a name, possibly followed by any of: -- - an open-paren, and anything up to the matching paren -- - an open-bracket, and anything up to the matching bracket -- - a period and a name -- any number of times, is evaluated as a Python expression. -- -- 2. A dollar sign immediately followed by an open-brace, and -- anything up to the matching close-brace, is evaluated as -- a Python expression. -- -- 3. Outside of the expressions described in the above two rules, -- two dollar signs in a row give you one literal dollar sign. -- -- Optional arguments: -- -- - codec('utf_8'): a string containing the name of a valid Python -- codec. -- -- - encoding_errors('backslashreplace'): a string with a valid error handling -- policy. See the codecs module documentation for details. -- -- These are used to encode the format string if a call to str() fails on -- the expanded result.""" -- -- if not isinstance(format,basestring): -- raise TypeError, "needs string initializer" -- self.format = format -- self.codec = codec -- self.encoding_errors = encoding_errors -- -- namechars = "abcdefghijklmnopqrstuvwxyz" \ -- "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; -- chunks = [] -- pos = 0 -- -- while 1: -- dollar = string.find(format, "$", pos) -- if dollar < 0: break -- nextchar = format[dollar+1] -- -- if nextchar == "{": -- chunks.append((0, format[pos:dollar])) -- pos, level = dollar+2, 1 -- while level: -- match, pos = matchorfail(format, pos) -- tstart, tend = match.regs[3] -- token = format[tstart:tend] -- if token == "{": level = level+1 -- elif token == "}": level = level-1 -- chunks.append((1, format[dollar+2:pos-1])) -- -- elif nextchar in namechars: -- chunks.append((0, format[pos:dollar])) -- match, pos = matchorfail(format, dollar+1) -- while pos < len(format): -- if format[pos] == "." and \ -- pos+1 < len(format) and format[pos+1] in namechars: -- match, pos = matchorfail(format, pos+1) -- elif format[pos] in "([": -- pos, level = pos+1, 1 -- while level: -- match, pos = matchorfail(format, pos) -- tstart, tend = match.regs[3] -- token = format[tstart:tend] -- if token[0] in "([": level = level+1 -- elif token[0] in ")]": level = level-1 -- else: break -- chunks.append((1, format[dollar+1:pos])) -- -- else: -- chunks.append((0, format[pos:dollar+1])) -- pos = dollar + 1 + (nextchar == "$") -- -- if pos < len(format): chunks.append((0, format[pos:])) -- self.chunks = chunks -- -- def __repr__(self): -- return "" % repr(self.format) -- -- def _str(self,glob,loc): -- """Evaluate to a string in the given globals/locals. -- -- The final output is built by calling str(), but if this fails, the -- result is encoded with the instance's codec and error handling policy, -- via a call to out.encode(self.codec,self.encoding_errors)""" -- result = [] -- app = result.append -- for live, chunk in self.chunks: -- if live: app(str(eval(chunk,glob,loc))) -- else: app(chunk) -- out = ''.join(result) -- try: -- return str(out) -- except UnicodeError: -- return out.encode(self.codec,self.encoding_errors) -- -- def __str__(self): -- """Evaluate and substitute the appropriate parts of the string.""" -- -- # We need to skip enough frames to get to the actual caller outside of -- # Itpl. -- frame = sys._getframe(1) -- while frame.f_globals["__name__"] == __name__: frame = frame.f_back -- loc, glob = frame.f_locals, frame.f_globals -- -- return self._str(glob,loc) -- --class ItplNS(Itpl): -- """Class representing a string with interpolation abilities. -- -- This inherits from Itpl, but at creation time a namespace is provided -- where the evaluation will occur. The interpolation becomes a bit more -- efficient, as no traceback needs to be extracte. It also allows the -- caller to supply a different namespace for the interpolation to occur than -- its own.""" -- -- def __init__(self, format,globals,locals=None, -- codec='utf_8',encoding_errors='backslashreplace'): -- """ItplNS(format,globals[,locals]) -> interpolating string instance. -- -- This constructor, besides a format string, takes a globals dictionary -- and optionally a locals (which defaults to globals if not provided). -- -- For further details, see the Itpl constructor.""" -- -- if locals is None: -- locals = globals -- self.globals = globals -- self.locals = locals -- Itpl.__init__(self,format,codec,encoding_errors) -- -- def __str__(self): -- """Evaluate and substitute the appropriate parts of the string.""" -- return self._str(self.globals,self.locals) -- -- def __repr__(self): -- return "" % repr(self.format) -- --# utilities for fast printing --def itpl(text): return str(Itpl(text)) --def printpl(text): print itpl(text) --# versions with namespace --def itplns(text,globals,locals=None): return str(ItplNS(text,globals,locals)) --def printplns(text,globals,locals=None): print itplns(text,globals,locals) -- --class ItplFile: -- """A file object that filters each write() through an interpolator.""" -- def __init__(self, file): self.file = file -- def __repr__(self): return "" -- def __getattr__(self, attr): return getattr(self.file, attr) -- def write(self, text): self.file.write(str(Itpl(text))) -- --def filter(file=sys.stdout): -- """Return an ItplFile that filters writes to the given file object. -- -- 'file = filter(file)' replaces 'file' with a filtered object that -- has a write() method. When called with no argument, this creates -- a filter to sys.stdout.""" -- return ItplFile(file) -- --def unfilter(ifile=None): -- """Return the original file that corresponds to the given ItplFile. -- -- 'file = unfilter(file)' undoes the effect of 'file = filter(file)'. -- 'sys.stdout = unfilter()' undoes the effect of 'sys.stdout = filter()'.""" -- return ifile and ifile.file or sys.stdout.file Index: ipython-0.10/IPython/external/mglob/__init__.py =================================================================== --- /dev/null @@ -10416,474 +50,6 @@ Index: ipython-0.10/IPython/external/mglob/__init__.py + from mglob import * +except ImportError: + from _mglob import * -Index: ipython-0.10/IPython/external/mglob/_mglob.py -=================================================================== ---- /dev/null -+++ ipython-0.10/IPython/external/mglob/_mglob.py -@@ -0,0 +1,229 @@ -+#!/usr/bin/env python -+ -+r""" mglob - enhanced file list expansion module -+ -+Use as stand-alone utility (for xargs, `backticks` etc.), -+or a globbing library for own python programs. Globbing the sys.argv is something -+that almost every Windows script has to perform manually, and this module is here -+to help with that task. Also Unix users will benefit from enhanced modes -+such as recursion, exclusion, directory omission... -+ -+Unlike glob.glob, directories are not included in the glob unless specified -+with 'dir:' -+ -+'expand' is the function to use in python programs. Typical use -+to expand argv (esp. in windows):: -+ -+ try: -+ import mglob -+ files = mglob.expand(sys.argv[1:]) -+ except ImportError: -+ print "mglob not found; try 'easy_install mglob' for extra features" -+ files = sys.argv[1:] -+ -+Note that for unix, shell expands *normal* wildcards (*.cpp, etc.) in argv. -+Therefore, you might want to use quotes with normal wildcards to prevent this -+expansion, in order for mglob to see the wildcards and get the wanted behaviour. -+Not quoting the wildcards is harmless and typically has equivalent results, though. -+ -+Author: Ville Vainio -+License: MIT Open Source license -+ -+""" -+ -+#Assigned in variable for "usage" printing convenience" -+ -+globsyntax = """\ -+ This program allows specifying filenames with "mglob" mechanism. -+ Supported syntax in globs (wilcard matching patterns):: -+ -+ *.cpp ?ellowo* -+ - obvious. Differs from normal glob in that dirs are not included. -+ Unix users might want to write this as: "*.cpp" "?ellowo*" -+ rec:/usr/share=*.txt,*.doc -+ - get all *.txt and *.doc under /usr/share, -+ recursively -+ rec:/usr/share -+ - All files under /usr/share, recursively -+ rec:*.py -+ - All .py files under current working dir, recursively -+ foo -+ - File or dir foo -+ !*.bak readme* -+ - readme*, exclude files ending with .bak -+ !.svn/ !.hg/ !*_Data/ rec:. -+ - Skip .svn, .hg, foo_Data dirs (and their subdirs) in recurse. -+ Trailing / is the key, \ does not work! Use !.*/ for all hidden. -+ dir:foo -+ - the directory foo if it exists (not files in foo) -+ dir:* -+ - all directories in current folder -+ foo.py bar.* !h* rec:*.py -+ - Obvious. !h* exclusion only applies for rec:*.py. -+ foo.py is *not* included twice. -+ @filelist.txt -+ - All files listed in 'filelist.txt' file, on separate lines. -+ "cont:class \wak:" rec:*.py -+ - Match files containing regexp. Applies to subsequent files. -+ note quotes because of whitespace. -+ """ -+ -+ -+__version__ = "0.2" -+ -+ -+import os,glob,fnmatch,sys,re -+ -+def expand(flist,exp_dirs = False): -+ """ Expand the glob(s) in flist. -+ -+ flist may be either a whitespace-separated list of globs/files -+ or an array of globs/files. -+ -+ if exp_dirs is true, directory names in glob are expanded to the files -+ contained in them - otherwise, directory names are returned as is. -+ -+ """ -+ if isinstance(flist, basestring): -+ import shlex -+ flist = shlex.split(flist) -+ done_set = set() -+ denied_set = set() -+ cont_set = set() -+ cur_rejected_dirs = set() -+ -+ def recfind(p, pats = ["*"]): -+ denied_dirs = [os.path.dirname(d) for d in denied_set if d.endswith("/")] -+ for (dp,dnames,fnames) in os.walk(p): -+ # see if we should ignore the whole directory -+ dp_norm = dp.replace("\\","/") + "/" -+ deny = False -+ # do not traverse under already rejected dirs -+ for d in cur_rejected_dirs: -+ if dp.startswith(d): -+ deny = True -+ break -+ if deny: -+ continue -+ -+ -+ #print "dp",dp -+ bname = os.path.basename(dp) -+ for deny_pat in denied_dirs: -+ if fnmatch.fnmatch( bname, deny_pat): -+ deny = True -+ cur_rejected_dirs.add(dp) -+ break -+ if deny: -+ continue -+ -+ -+ for f in fnames: -+ matched = False -+ for p in pats: -+ if fnmatch.fnmatch(f,p): -+ matched = True -+ break -+ if matched: -+ yield os.path.join(dp,f) -+ -+ def once_filter(seq): -+ for it in seq: -+ p = os.path.abspath(it) -+ if p in done_set: -+ continue -+ done_set.add(p) -+ deny = False -+ for deny_pat in denied_set: -+ if fnmatch.fnmatch(os.path.basename(p), deny_pat): -+ deny = True -+ break -+ if cont_set: -+ try: -+ cont = open(p).read() -+ except IOError: -+ # deny -+ continue -+ for pat in cont_set: -+ if not re.search(pat,cont, re.IGNORECASE): -+ deny = True -+ break -+ -+ if not deny: -+ yield it -+ return -+ -+ res = [] -+ -+ for ent in flist: -+ ent = os.path.expanduser(os.path.expandvars(ent)) -+ if ent.lower().startswith('rec:'): -+ fields = ent[4:].split('=') -+ if len(fields) == 2: -+ pth, patlist = fields -+ elif len(fields) == 1: -+ if os.path.isdir(fields[0]): -+ # single arg is dir -+ pth, patlist = fields[0], '*' -+ else: -+ # single arg is pattern -+ pth, patlist = '.', fields[0] -+ -+ elif len(fields) == 0: -+ pth, pathlist = '.','*' -+ -+ pats = patlist.split(',') -+ res.extend(once_filter(recfind(pth, pats))) -+ # filelist -+ elif ent.startswith('@') and os.path.isfile(ent[1:]): -+ res.extend(once_filter(open(ent[1:]).read().splitlines())) -+ # exclusion -+ elif ent.startswith('!'): -+ denied_set.add(ent[1:]) -+ # glob only dirs -+ elif ent.lower().startswith('dir:'): -+ res.extend(once_filter(filter(os.path.isdir,glob.glob(ent[4:])))) -+ elif ent.lower().startswith('cont:'): -+ cont_set.add(ent[5:]) -+ # get all files in the specified dir -+ elif os.path.isdir(ent) and exp_dirs: -+ res.extend(once_filter(filter(os.path.isfile,glob.glob(ent + os.sep+"*")))) -+ -+ # glob only files -+ -+ elif '*' in ent or '?' in ent: -+ res.extend(once_filter(filter(os.path.isfile,glob.glob(ent)))) -+ -+ else: -+ res.extend(once_filter([ent])) -+ return res -+ -+ -+def test(): -+ assert ( -+ expand("*.py ~/.ipython/*.py rec:/usr/share/doc-base") == -+ expand( ['*.py', '~/.ipython/*.py', 'rec:/usr/share/doc-base'] ) -+ ) -+ -+def main(): -+ if len(sys.argv) < 2: -+ print globsyntax -+ return -+ -+ print "\n".join(expand(sys.argv[1:])), -+ -+def mglob_f(self, arg): -+ from IPython.genutils import SList -+ if arg.strip(): -+ return SList(expand(arg)) -+ print "Please specify pattern!" -+ print globsyntax -+ -+def init_ipython(ip): -+ """ register %mglob for IPython """ -+ mglob_f.__doc__ = globsyntax -+ ip.expose_magic("mglob",mglob_f) -+ -+# test() -+if __name__ == "__main__": -+ main() -Index: ipython-0.10/IPython/external/mglob.py -=================================================================== ---- ipython-0.10.orig/IPython/external/mglob.py -+++ /dev/null -@@ -1,229 +0,0 @@ --#!/usr/bin/env python -- --r""" mglob - enhanced file list expansion module -- --Use as stand-alone utility (for xargs, `backticks` etc.), --or a globbing library for own python programs. Globbing the sys.argv is something --that almost every Windows script has to perform manually, and this module is here --to help with that task. Also Unix users will benefit from enhanced modes --such as recursion, exclusion, directory omission... -- --Unlike glob.glob, directories are not included in the glob unless specified --with 'dir:' -- --'expand' is the function to use in python programs. Typical use --to expand argv (esp. in windows):: -- -- try: -- import mglob -- files = mglob.expand(sys.argv[1:]) -- except ImportError: -- print "mglob not found; try 'easy_install mglob' for extra features" -- files = sys.argv[1:] -- --Note that for unix, shell expands *normal* wildcards (*.cpp, etc.) in argv. --Therefore, you might want to use quotes with normal wildcards to prevent this --expansion, in order for mglob to see the wildcards and get the wanted behaviour. --Not quoting the wildcards is harmless and typically has equivalent results, though. -- --Author: Ville Vainio --License: MIT Open Source license -- --""" -- --#Assigned in variable for "usage" printing convenience" -- --globsyntax = """\ -- This program allows specifying filenames with "mglob" mechanism. -- Supported syntax in globs (wilcard matching patterns):: -- -- *.cpp ?ellowo* -- - obvious. Differs from normal glob in that dirs are not included. -- Unix users might want to write this as: "*.cpp" "?ellowo*" -- rec:/usr/share=*.txt,*.doc -- - get all *.txt and *.doc under /usr/share, -- recursively -- rec:/usr/share -- - All files under /usr/share, recursively -- rec:*.py -- - All .py files under current working dir, recursively -- foo -- - File or dir foo -- !*.bak readme* -- - readme*, exclude files ending with .bak -- !.svn/ !.hg/ !*_Data/ rec:. -- - Skip .svn, .hg, foo_Data dirs (and their subdirs) in recurse. -- Trailing / is the key, \ does not work! Use !.*/ for all hidden. -- dir:foo -- - the directory foo if it exists (not files in foo) -- dir:* -- - all directories in current folder -- foo.py bar.* !h* rec:*.py -- - Obvious. !h* exclusion only applies for rec:*.py. -- foo.py is *not* included twice. -- @filelist.txt -- - All files listed in 'filelist.txt' file, on separate lines. -- "cont:class \wak:" rec:*.py -- - Match files containing regexp. Applies to subsequent files. -- note quotes because of whitespace. -- """ -- -- --__version__ = "0.2" -- -- --import os,glob,fnmatch,sys,re -- --def expand(flist,exp_dirs = False): -- """ Expand the glob(s) in flist. -- -- flist may be either a whitespace-separated list of globs/files -- or an array of globs/files. -- -- if exp_dirs is true, directory names in glob are expanded to the files -- contained in them - otherwise, directory names are returned as is. -- -- """ -- if isinstance(flist, basestring): -- import shlex -- flist = shlex.split(flist) -- done_set = set() -- denied_set = set() -- cont_set = set() -- cur_rejected_dirs = set() -- -- def recfind(p, pats = ["*"]): -- denied_dirs = [os.path.dirname(d) for d in denied_set if d.endswith("/")] -- for (dp,dnames,fnames) in os.walk(p): -- # see if we should ignore the whole directory -- dp_norm = dp.replace("\\","/") + "/" -- deny = False -- # do not traverse under already rejected dirs -- for d in cur_rejected_dirs: -- if dp.startswith(d): -- deny = True -- break -- if deny: -- continue -- -- -- #print "dp",dp -- bname = os.path.basename(dp) -- for deny_pat in denied_dirs: -- if fnmatch.fnmatch( bname, deny_pat): -- deny = True -- cur_rejected_dirs.add(dp) -- break -- if deny: -- continue -- -- -- for f in fnames: -- matched = False -- for p in pats: -- if fnmatch.fnmatch(f,p): -- matched = True -- break -- if matched: -- yield os.path.join(dp,f) -- -- def once_filter(seq): -- for it in seq: -- p = os.path.abspath(it) -- if p in done_set: -- continue -- done_set.add(p) -- deny = False -- for deny_pat in denied_set: -- if fnmatch.fnmatch(os.path.basename(p), deny_pat): -- deny = True -- break -- if cont_set: -- try: -- cont = open(p).read() -- except IOError: -- # deny -- continue -- for pat in cont_set: -- if not re.search(pat,cont, re.IGNORECASE): -- deny = True -- break -- -- if not deny: -- yield it -- return -- -- res = [] -- -- for ent in flist: -- ent = os.path.expanduser(os.path.expandvars(ent)) -- if ent.lower().startswith('rec:'): -- fields = ent[4:].split('=') -- if len(fields) == 2: -- pth, patlist = fields -- elif len(fields) == 1: -- if os.path.isdir(fields[0]): -- # single arg is dir -- pth, patlist = fields[0], '*' -- else: -- # single arg is pattern -- pth, patlist = '.', fields[0] -- -- elif len(fields) == 0: -- pth, pathlist = '.','*' -- -- pats = patlist.split(',') -- res.extend(once_filter(recfind(pth, pats))) -- # filelist -- elif ent.startswith('@') and os.path.isfile(ent[1:]): -- res.extend(once_filter(open(ent[1:]).read().splitlines())) -- # exclusion -- elif ent.startswith('!'): -- denied_set.add(ent[1:]) -- # glob only dirs -- elif ent.lower().startswith('dir:'): -- res.extend(once_filter(filter(os.path.isdir,glob.glob(ent[4:])))) -- elif ent.lower().startswith('cont:'): -- cont_set.add(ent[5:]) -- # get all files in the specified dir -- elif os.path.isdir(ent) and exp_dirs: -- res.extend(once_filter(filter(os.path.isfile,glob.glob(ent + os.sep+"*")))) -- -- # glob only files -- -- elif '*' in ent or '?' in ent: -- res.extend(once_filter(filter(os.path.isfile,glob.glob(ent)))) -- -- else: -- res.extend(once_filter([ent])) -- return res -- -- --def test(): -- assert ( -- expand("*.py ~/.ipython/*.py rec:/usr/share/doc-base") == -- expand( ['*.py', '~/.ipython/*.py', 'rec:/usr/share/doc-base'] ) -- ) -- --def main(): -- if len(sys.argv) < 2: -- print globsyntax -- return -- -- print "\n".join(expand(sys.argv[1:])), -- --def mglob_f(self, arg): -- from IPython.genutils import SList -- if arg.strip(): -- return SList(expand(arg)) -- print "Please specify pattern!" -- print globsyntax -- --def init_ipython(ip): -- """ register %mglob for IPython """ -- mglob_f.__doc__ = globsyntax -- ip.expose_magic("mglob",mglob_f) -- --# test() --if __name__ == "__main__": -- main() Index: ipython-0.10/IPython/external/path/__init__.py =================================================================== --- /dev/null @@ -10893,1962 +59,6 @@ Index: ipython-0.10/IPython/external/path/__init__.py + from path import * +except ImportError: + from _path import * -Index: ipython-0.10/IPython/external/path/_path.py -=================================================================== ---- /dev/null -+++ ipython-0.10/IPython/external/path/_path.py -@@ -0,0 +1,973 @@ -+""" path.py - An object representing a path to a file or directory. -+ -+Example: -+ -+from IPython.external.path import path -+d = path('/home/guido/bin') -+for f in d.files('*.py'): -+ f.chmod(0755) -+ -+This module requires Python 2.2 or later. -+ -+ -+URL: http://www.jorendorff.com/articles/python/path -+Author: Jason Orendorff (and others - see the url!) -+Date: 9 Mar 2007 -+""" -+ -+ -+# TODO -+# - Tree-walking functions don't avoid symlink loops. Matt Harrison -+# sent me a patch for this. -+# - Bug in write_text(). It doesn't support Universal newline mode. -+# - Better error message in listdir() when self isn't a -+# directory. (On Windows, the error message really sucks.) -+# - Make sure everything has a good docstring. -+# - Add methods for regex find and replace. -+# - guess_content_type() method? -+# - Perhaps support arguments to touch(). -+ -+from __future__ import generators -+ -+import sys, warnings, os, fnmatch, glob, shutil, codecs -+# deprecated in python 2.6 -+warnings.filterwarnings('ignore', r'.*md5.*') -+import md5 -+ -+__version__ = '2.2' -+__all__ = ['path'] -+ -+# Platform-specific support for path.owner -+if os.name == 'nt': -+ try: -+ import win32security -+ except ImportError: -+ win32security = None -+else: -+ try: -+ import pwd -+ except ImportError: -+ pwd = None -+ -+# Pre-2.3 support. Are unicode filenames supported? -+_base = str -+_getcwd = os.getcwd -+try: -+ if os.path.supports_unicode_filenames: -+ _base = unicode -+ _getcwd = os.getcwdu -+except AttributeError: -+ pass -+ -+# Pre-2.3 workaround for booleans -+try: -+ True, False -+except NameError: -+ True, False = 1, 0 -+ -+# Pre-2.3 workaround for basestring. -+try: -+ basestring -+except NameError: -+ basestring = (str, unicode) -+ -+# Universal newline support -+_textmode = 'r' -+if hasattr(file, 'newlines'): -+ _textmode = 'U' -+ -+ -+class TreeWalkWarning(Warning): -+ pass -+ -+class path(_base): -+ """ Represents a filesystem path. -+ -+ For documentation on individual methods, consult their -+ counterparts in os.path. -+ """ -+ -+ # --- Special Python methods. -+ -+ def __repr__(self): -+ return 'path(%s)' % _base.__repr__(self) -+ -+ # Adding a path and a string yields a path. -+ def __add__(self, more): -+ try: -+ resultStr = _base.__add__(self, more) -+ except TypeError: #Python bug -+ resultStr = NotImplemented -+ if resultStr is NotImplemented: -+ return resultStr -+ return self.__class__(resultStr) -+ -+ def __radd__(self, other): -+ if isinstance(other, basestring): -+ return self.__class__(other.__add__(self)) -+ else: -+ return NotImplemented -+ -+ # The / operator joins paths. -+ def __div__(self, rel): -+ """ fp.__div__(rel) == fp / rel == fp.joinpath(rel) -+ -+ Join two path components, adding a separator character if -+ needed. -+ """ -+ return self.__class__(os.path.join(self, rel)) -+ -+ # Make the / operator work even when true division is enabled. -+ __truediv__ = __div__ -+ -+ def getcwd(cls): -+ """ Return the current working directory as a path object. """ -+ return cls(_getcwd()) -+ getcwd = classmethod(getcwd) -+ -+ -+ # --- Operations on path strings. -+ -+ isabs = os.path.isabs -+ def abspath(self): return self.__class__(os.path.abspath(self)) -+ def normcase(self): return self.__class__(os.path.normcase(self)) -+ def normpath(self): return self.__class__(os.path.normpath(self)) -+ def realpath(self): return self.__class__(os.path.realpath(self)) -+ def expanduser(self): return self.__class__(os.path.expanduser(self)) -+ def expandvars(self): return self.__class__(os.path.expandvars(self)) -+ def dirname(self): return self.__class__(os.path.dirname(self)) -+ basename = os.path.basename -+ -+ def expand(self): -+ """ Clean up a filename by calling expandvars(), -+ expanduser(), and normpath() on it. -+ -+ This is commonly everything needed to clean up a filename -+ read from a configuration file, for example. -+ """ -+ return self.expandvars().expanduser().normpath() -+ -+ def _get_namebase(self): -+ base, ext = os.path.splitext(self.name) -+ return base -+ -+ def _get_ext(self): -+ f, ext = os.path.splitext(_base(self)) -+ return ext -+ -+ def _get_drive(self): -+ drive, r = os.path.splitdrive(self) -+ return self.__class__(drive) -+ -+ parent = property( -+ dirname, None, None, -+ """ This path's parent directory, as a new path object. -+ -+ For example, path('/usr/local/lib/libpython.so').parent == path('/usr/local/lib') -+ """) -+ -+ name = property( -+ basename, None, None, -+ """ The name of this file or directory without the full path. -+ -+ For example, path('/usr/local/lib/libpython.so').name == 'libpython.so' -+ """) -+ -+ namebase = property( -+ _get_namebase, None, None, -+ """ The same as path.name, but with one file extension stripped off. -+ -+ For example, path('/home/guido/python.tar.gz').name == 'python.tar.gz', -+ but path('/home/guido/python.tar.gz').namebase == 'python.tar' -+ """) -+ -+ ext = property( -+ _get_ext, None, None, -+ """ The file extension, for example '.py'. """) -+ -+ drive = property( -+ _get_drive, None, None, -+ """ The drive specifier, for example 'C:'. -+ This is always empty on systems that don't use drive specifiers. -+ """) -+ -+ def splitpath(self): -+ """ p.splitpath() -> Return (p.parent, p.name). """ -+ parent, child = os.path.split(self) -+ return self.__class__(parent), child -+ -+ def splitdrive(self): -+ """ p.splitdrive() -> Return (p.drive, ). -+ -+ Split the drive specifier from this path. If there is -+ no drive specifier, p.drive is empty, so the return value -+ is simply (path(''), p). This is always the case on Unix. -+ """ -+ drive, rel = os.path.splitdrive(self) -+ return self.__class__(drive), rel -+ -+ def splitext(self): -+ """ p.splitext() -> Return (p.stripext(), p.ext). -+ -+ Split the filename extension from this path and return -+ the two parts. Either part may be empty. -+ -+ The extension is everything from '.' to the end of the -+ last path segment. This has the property that if -+ (a, b) == p.splitext(), then a + b == p. -+ """ -+ filename, ext = os.path.splitext(self) -+ return self.__class__(filename), ext -+ -+ def stripext(self): -+ """ p.stripext() -> Remove one file extension from the path. -+ -+ For example, path('/home/guido/python.tar.gz').stripext() -+ returns path('/home/guido/python.tar'). -+ """ -+ return self.splitext()[0] -+ -+ if hasattr(os.path, 'splitunc'): -+ def splitunc(self): -+ unc, rest = os.path.splitunc(self) -+ return self.__class__(unc), rest -+ -+ def _get_uncshare(self): -+ unc, r = os.path.splitunc(self) -+ return self.__class__(unc) -+ -+ uncshare = property( -+ _get_uncshare, None, None, -+ """ The UNC mount point for this path. -+ This is empty for paths on local drives. """) -+ -+ def joinpath(self, *args): -+ """ Join two or more path components, adding a separator -+ character (os.sep) if needed. Returns a new path -+ object. -+ """ -+ return self.__class__(os.path.join(self, *args)) -+ -+ def splitall(self): -+ r""" Return a list of the path components in this path. -+ -+ The first item in the list will be a path. Its value will be -+ either os.curdir, os.pardir, empty, or the root directory of -+ this path (for example, '/' or 'C:\\'). The other items in -+ the list will be strings. -+ -+ path.path.joinpath(*result) will yield the original path. -+ """ -+ parts = [] -+ loc = self -+ while loc != os.curdir and loc != os.pardir: -+ prev = loc -+ loc, child = prev.splitpath() -+ if loc == prev: -+ break -+ parts.append(child) -+ parts.append(loc) -+ parts.reverse() -+ return parts -+ -+ def relpath(self): -+ """ Return this path as a relative path, -+ based from the current working directory. -+ """ -+ cwd = self.__class__(os.getcwd()) -+ return cwd.relpathto(self) -+ -+ def relpathto(self, dest): -+ """ Return a relative path from self to dest. -+ -+ If there is no relative path from self to dest, for example if -+ they reside on different drives in Windows, then this returns -+ dest.abspath(). -+ """ -+ origin = self.abspath() -+ dest = self.__class__(dest).abspath() -+ -+ orig_list = origin.normcase().splitall() -+ # Don't normcase dest! We want to preserve the case. -+ dest_list = dest.splitall() -+ -+ if orig_list[0] != os.path.normcase(dest_list[0]): -+ # Can't get here from there. -+ return dest -+ -+ # Find the location where the two paths start to differ. -+ i = 0 -+ for start_seg, dest_seg in zip(orig_list, dest_list): -+ if start_seg != os.path.normcase(dest_seg): -+ break -+ i += 1 -+ -+ # Now i is the point where the two paths diverge. -+ # Need a certain number of "os.pardir"s to work up -+ # from the origin to the point of divergence. -+ segments = [os.pardir] * (len(orig_list) - i) -+ # Need to add the diverging part of dest_list. -+ segments += dest_list[i:] -+ if len(segments) == 0: -+ # If they happen to be identical, use os.curdir. -+ relpath = os.curdir -+ else: -+ relpath = os.path.join(*segments) -+ return self.__class__(relpath) -+ -+ # --- Listing, searching, walking, and matching -+ -+ def listdir(self, pattern=None): -+ """ D.listdir() -> List of items in this directory. -+ -+ Use D.files() or D.dirs() instead if you want a listing -+ of just files or just subdirectories. -+ -+ The elements of the list are path objects. -+ -+ With the optional 'pattern' argument, this only lists -+ items whose names match the given pattern. -+ """ -+ names = os.listdir(self) -+ if pattern is not None: -+ names = fnmatch.filter(names, pattern) -+ return [self / child for child in names] -+ -+ def dirs(self, pattern=None): -+ """ D.dirs() -> List of this directory's subdirectories. -+ -+ The elements of the list are path objects. -+ This does not walk recursively into subdirectories -+ (but see path.walkdirs). -+ -+ With the optional 'pattern' argument, this only lists -+ directories whose names match the given pattern. For -+ example, d.dirs('build-*'). -+ """ -+ return [p for p in self.listdir(pattern) if p.isdir()] -+ -+ def files(self, pattern=None): -+ """ D.files() -> List of the files in this directory. -+ -+ The elements of the list are path objects. -+ This does not walk into subdirectories (see path.walkfiles). -+ -+ With the optional 'pattern' argument, this only lists files -+ whose names match the given pattern. For example, -+ d.files('*.pyc'). -+ """ -+ -+ return [p for p in self.listdir(pattern) if p.isfile()] -+ -+ def walk(self, pattern=None, errors='strict'): -+ """ D.walk() -> iterator over files and subdirs, recursively. -+ -+ The iterator yields path objects naming each child item of -+ this directory and its descendants. This requires that -+ D.isdir(). -+ -+ This performs a depth-first traversal of the directory tree. -+ Each directory is returned just before all its children. -+ -+ The errors= keyword argument controls behavior when an -+ error occurs. The default is 'strict', which causes an -+ exception. The other allowed values are 'warn', which -+ reports the error via warnings.warn(), and 'ignore'. -+ """ -+ if errors not in ('strict', 'warn', 'ignore'): -+ raise ValueError("invalid errors parameter") -+ -+ try: -+ childList = self.listdir() -+ except Exception: -+ if errors == 'ignore': -+ return -+ elif errors == 'warn': -+ warnings.warn( -+ "Unable to list directory '%s': %s" -+ % (self, sys.exc_info()[1]), -+ TreeWalkWarning) -+ return -+ else: -+ raise -+ -+ for child in childList: -+ if pattern is None or child.fnmatch(pattern): -+ yield child -+ try: -+ isdir = child.isdir() -+ except Exception: -+ if errors == 'ignore': -+ isdir = False -+ elif errors == 'warn': -+ warnings.warn( -+ "Unable to access '%s': %s" -+ % (child, sys.exc_info()[1]), -+ TreeWalkWarning) -+ isdir = False -+ else: -+ raise -+ -+ if isdir: -+ for item in child.walk(pattern, errors): -+ yield item -+ -+ def walkdirs(self, pattern=None, errors='strict'): -+ """ D.walkdirs() -> iterator over subdirs, recursively. -+ -+ With the optional 'pattern' argument, this yields only -+ directories whose names match the given pattern. For -+ example, mydir.walkdirs('*test') yields only directories -+ with names ending in 'test'. -+ -+ The errors= keyword argument controls behavior when an -+ error occurs. The default is 'strict', which causes an -+ exception. The other allowed values are 'warn', which -+ reports the error via warnings.warn(), and 'ignore'. -+ """ -+ if errors not in ('strict', 'warn', 'ignore'): -+ raise ValueError("invalid errors parameter") -+ -+ try: -+ dirs = self.dirs() -+ except Exception: -+ if errors == 'ignore': -+ return -+ elif errors == 'warn': -+ warnings.warn( -+ "Unable to list directory '%s': %s" -+ % (self, sys.exc_info()[1]), -+ TreeWalkWarning) -+ return -+ else: -+ raise -+ -+ for child in dirs: -+ if pattern is None or child.fnmatch(pattern): -+ yield child -+ for subsubdir in child.walkdirs(pattern, errors): -+ yield subsubdir -+ -+ def walkfiles(self, pattern=None, errors='strict'): -+ """ D.walkfiles() -> iterator over files in D, recursively. -+ -+ The optional argument, pattern, limits the results to files -+ with names that match the pattern. For example, -+ mydir.walkfiles('*.tmp') yields only files with the .tmp -+ extension. -+ """ -+ if errors not in ('strict', 'warn', 'ignore'): -+ raise ValueError("invalid errors parameter") -+ -+ try: -+ childList = self.listdir() -+ except Exception: -+ if errors == 'ignore': -+ return -+ elif errors == 'warn': -+ warnings.warn( -+ "Unable to list directory '%s': %s" -+ % (self, sys.exc_info()[1]), -+ TreeWalkWarning) -+ return -+ else: -+ raise -+ -+ for child in childList: -+ try: -+ isfile = child.isfile() -+ isdir = not isfile and child.isdir() -+ except: -+ if errors == 'ignore': -+ continue -+ elif errors == 'warn': -+ warnings.warn( -+ "Unable to access '%s': %s" -+ % (self, sys.exc_info()[1]), -+ TreeWalkWarning) -+ continue -+ else: -+ raise -+ -+ if isfile: -+ if pattern is None or child.fnmatch(pattern): -+ yield child -+ elif isdir: -+ for f in child.walkfiles(pattern, errors): -+ yield f -+ -+ def fnmatch(self, pattern): -+ """ Return True if self.name matches the given pattern. -+ -+ pattern - A filename pattern with wildcards, -+ for example '*.py'. -+ """ -+ return fnmatch.fnmatch(self.name, pattern) -+ -+ def glob(self, pattern): -+ """ Return a list of path objects that match the pattern. -+ -+ pattern - a path relative to this directory, with wildcards. -+ -+ For example, path('/users').glob('*/bin/*') returns a list -+ of all the files users have in their bin directories. -+ """ -+ cls = self.__class__ -+ return [cls(s) for s in glob.glob(_base(self / pattern))] -+ -+ -+ # --- Reading or writing an entire file at once. -+ -+ def open(self, mode='r'): -+ """ Open this file. Return a file object. """ -+ return file(self, mode) -+ -+ def bytes(self): -+ """ Open this file, read all bytes, return them as a string. """ -+ f = self.open('rb') -+ try: -+ return f.read() -+ finally: -+ f.close() -+ -+ def write_bytes(self, bytes, append=False): -+ """ Open this file and write the given bytes to it. -+ -+ Default behavior is to overwrite any existing file. -+ Call p.write_bytes(bytes, append=True) to append instead. -+ """ -+ if append: -+ mode = 'ab' -+ else: -+ mode = 'wb' -+ f = self.open(mode) -+ try: -+ f.write(bytes) -+ finally: -+ f.close() -+ -+ def text(self, encoding=None, errors='strict'): -+ r""" Open this file, read it in, return the content as a string. -+ -+ This uses 'U' mode in Python 2.3 and later, so '\r\n' and '\r' -+ are automatically translated to '\n'. -+ -+ Optional arguments: -+ -+ encoding - The Unicode encoding (or character set) of -+ the file. If present, the content of the file is -+ decoded and returned as a unicode object; otherwise -+ it is returned as an 8-bit str. -+ errors - How to handle Unicode errors; see help(str.decode) -+ for the options. Default is 'strict'. -+ """ -+ if encoding is None: -+ # 8-bit -+ f = self.open(_textmode) -+ try: -+ return f.read() -+ finally: -+ f.close() -+ else: -+ # Unicode -+ f = codecs.open(self, 'r', encoding, errors) -+ # (Note - Can't use 'U' mode here, since codecs.open -+ # doesn't support 'U' mode, even in Python 2.3.) -+ try: -+ t = f.read() -+ finally: -+ f.close() -+ return (t.replace(u'\r\n', u'\n') -+ .replace(u'\r\x85', u'\n') -+ .replace(u'\r', u'\n') -+ .replace(u'\x85', u'\n') -+ .replace(u'\u2028', u'\n')) -+ -+ def write_text(self, text, encoding=None, errors='strict', linesep=os.linesep, append=False): -+ r""" Write the given text to this file. -+ -+ The default behavior is to overwrite any existing file; -+ to append instead, use the 'append=True' keyword argument. -+ -+ There are two differences between path.write_text() and -+ path.write_bytes(): newline handling and Unicode handling. -+ See below. -+ -+ Parameters: -+ -+ - text - str/unicode - The text to be written. -+ -+ - encoding - str - The Unicode encoding that will be used. -+ This is ignored if 'text' isn't a Unicode string. -+ -+ - errors - str - How to handle Unicode encoding errors. -+ Default is 'strict'. See help(unicode.encode) for the -+ options. This is ignored if 'text' isn't a Unicode -+ string. -+ -+ - linesep - keyword argument - str/unicode - The sequence of -+ characters to be used to mark end-of-line. The default is -+ os.linesep. You can also specify None; this means to -+ leave all newlines as they are in 'text'. -+ -+ - append - keyword argument - bool - Specifies what to do if -+ the file already exists (True: append to the end of it; -+ False: overwrite it.) The default is False. -+ -+ -+ --- Newline handling. -+ -+ write_text() converts all standard end-of-line sequences -+ ('\n', '\r', and '\r\n') to your platform's default end-of-line -+ sequence (see os.linesep; on Windows, for example, the -+ end-of-line marker is '\r\n'). -+ -+ If you don't like your platform's default, you can override it -+ using the 'linesep=' keyword argument. If you specifically want -+ write_text() to preserve the newlines as-is, use 'linesep=None'. -+ -+ This applies to Unicode text the same as to 8-bit text, except -+ there are three additional standard Unicode end-of-line sequences: -+ u'\x85', u'\r\x85', and u'\u2028'. -+ -+ (This is slightly different from when you open a file for -+ writing with fopen(filename, "w") in C or file(filename, 'w') -+ in Python.) -+ -+ -+ --- Unicode -+ -+ If 'text' isn't Unicode, then apart from newline handling, the -+ bytes are written verbatim to the file. The 'encoding' and -+ 'errors' arguments are not used and must be omitted. -+ -+ If 'text' is Unicode, it is first converted to bytes using the -+ specified 'encoding' (or the default encoding if 'encoding' -+ isn't specified). The 'errors' argument applies only to this -+ conversion. -+ -+ """ -+ if isinstance(text, unicode): -+ if linesep is not None: -+ # Convert all standard end-of-line sequences to -+ # ordinary newline characters. -+ text = (text.replace(u'\r\n', u'\n') -+ .replace(u'\r\x85', u'\n') -+ .replace(u'\r', u'\n') -+ .replace(u'\x85', u'\n') -+ .replace(u'\u2028', u'\n')) -+ text = text.replace(u'\n', linesep) -+ if encoding is None: -+ encoding = sys.getdefaultencoding() -+ bytes = text.encode(encoding, errors) -+ else: -+ # It is an error to specify an encoding if 'text' is -+ # an 8-bit string. -+ assert encoding is None -+ -+ if linesep is not None: -+ text = (text.replace('\r\n', '\n') -+ .replace('\r', '\n')) -+ bytes = text.replace('\n', linesep) -+ -+ self.write_bytes(bytes, append) -+ -+ def lines(self, encoding=None, errors='strict', retain=True): -+ r""" Open this file, read all lines, return them in a list. -+ -+ Optional arguments: -+ encoding - The Unicode encoding (or character set) of -+ the file. The default is None, meaning the content -+ of the file is read as 8-bit characters and returned -+ as a list of (non-Unicode) str objects. -+ errors - How to handle Unicode errors; see help(str.decode) -+ for the options. Default is 'strict' -+ retain - If true, retain newline characters; but all newline -+ character combinations ('\r', '\n', '\r\n') are -+ translated to '\n'. If false, newline characters are -+ stripped off. Default is True. -+ -+ This uses 'U' mode in Python 2.3 and later. -+ """ -+ if encoding is None and retain: -+ f = self.open(_textmode) -+ try: -+ return f.readlines() -+ finally: -+ f.close() -+ else: -+ return self.text(encoding, errors).splitlines(retain) -+ -+ def write_lines(self, lines, encoding=None, errors='strict', -+ linesep=os.linesep, append=False): -+ r""" Write the given lines of text to this file. -+ -+ By default this overwrites any existing file at this path. -+ -+ This puts a platform-specific newline sequence on every line. -+ See 'linesep' below. -+ -+ lines - A list of strings. -+ -+ encoding - A Unicode encoding to use. This applies only if -+ 'lines' contains any Unicode strings. -+ -+ errors - How to handle errors in Unicode encoding. This -+ also applies only to Unicode strings. -+ -+ linesep - The desired line-ending. This line-ending is -+ applied to every line. If a line already has any -+ standard line ending ('\r', '\n', '\r\n', u'\x85', -+ u'\r\x85', u'\u2028'), that will be stripped off and -+ this will be used instead. The default is os.linesep, -+ which is platform-dependent ('\r\n' on Windows, '\n' on -+ Unix, etc.) Specify None to write the lines as-is, -+ like file.writelines(). -+ -+ Use the keyword argument append=True to append lines to the -+ file. The default is to overwrite the file. Warning: -+ When you use this with Unicode data, if the encoding of the -+ existing data in the file is different from the encoding -+ you specify with the encoding= parameter, the result is -+ mixed-encoding data, which can really confuse someone trying -+ to read the file later. -+ """ -+ if append: -+ mode = 'ab' -+ else: -+ mode = 'wb' -+ f = self.open(mode) -+ try: -+ for line in lines: -+ isUnicode = isinstance(line, unicode) -+ if linesep is not None: -+ # Strip off any existing line-end and add the -+ # specified linesep string. -+ if isUnicode: -+ if line[-2:] in (u'\r\n', u'\x0d\x85'): -+ line = line[:-2] -+ elif line[-1:] in (u'\r', u'\n', -+ u'\x85', u'\u2028'): -+ line = line[:-1] -+ else: -+ if line[-2:] == '\r\n': -+ line = line[:-2] -+ elif line[-1:] in ('\r', '\n'): -+ line = line[:-1] -+ line += linesep -+ if isUnicode: -+ if encoding is None: -+ encoding = sys.getdefaultencoding() -+ line = line.encode(encoding, errors) -+ f.write(line) -+ finally: -+ f.close() -+ -+ def read_md5(self): -+ """ Calculate the md5 hash for this file. -+ -+ This reads through the entire file. -+ """ -+ f = self.open('rb') -+ try: -+ m = md5.new() -+ while True: -+ d = f.read(8192) -+ if not d: -+ break -+ m.update(d) -+ finally: -+ f.close() -+ return m.digest() -+ -+ # --- Methods for querying the filesystem. -+ -+ exists = os.path.exists -+ isdir = os.path.isdir -+ isfile = os.path.isfile -+ islink = os.path.islink -+ ismount = os.path.ismount -+ -+ if hasattr(os.path, 'samefile'): -+ samefile = os.path.samefile -+ -+ getatime = os.path.getatime -+ atime = property( -+ getatime, None, None, -+ """ Last access time of the file. """) -+ -+ getmtime = os.path.getmtime -+ mtime = property( -+ getmtime, None, None, -+ """ Last-modified time of the file. """) -+ -+ if hasattr(os.path, 'getctime'): -+ getctime = os.path.getctime -+ ctime = property( -+ getctime, None, None, -+ """ Creation time of the file. """) -+ -+ getsize = os.path.getsize -+ size = property( -+ getsize, None, None, -+ """ Size of the file, in bytes. """) -+ -+ if hasattr(os, 'access'): -+ def access(self, mode): -+ """ Return true if current user has access to this path. -+ -+ mode - One of the constants os.F_OK, os.R_OK, os.W_OK, os.X_OK -+ """ -+ return os.access(self, mode) -+ -+ def stat(self): -+ """ Perform a stat() system call on this path. """ -+ return os.stat(self) -+ -+ def lstat(self): -+ """ Like path.stat(), but do not follow symbolic links. """ -+ return os.lstat(self) -+ -+ def get_owner(self): -+ r""" Return the name of the owner of this file or directory. -+ -+ This follows symbolic links. -+ -+ On Windows, this returns a name of the form ur'DOMAIN\User Name'. -+ On Windows, a group can own a file or directory. -+ """ -+ if os.name == 'nt': -+ if win32security is None: -+ raise Exception("path.owner requires win32all to be installed") -+ desc = win32security.GetFileSecurity( -+ self, win32security.OWNER_SECURITY_INFORMATION) -+ sid = desc.GetSecurityDescriptorOwner() -+ account, domain, typecode = win32security.LookupAccountSid(None, sid) -+ return domain + u'\\' + account -+ else: -+ if pwd is None: -+ raise NotImplementedError("path.owner is not implemented on this platform.") -+ st = self.stat() -+ return pwd.getpwuid(st.st_uid).pw_name -+ -+ owner = property( -+ get_owner, None, None, -+ """ Name of the owner of this file or directory. """) -+ -+ if hasattr(os, 'statvfs'): -+ def statvfs(self): -+ """ Perform a statvfs() system call on this path. """ -+ return os.statvfs(self) -+ -+ if hasattr(os, 'pathconf'): -+ def pathconf(self, name): -+ return os.pathconf(self, name) -+ -+ -+ # --- Modifying operations on files and directories -+ -+ def utime(self, times): -+ """ Set the access and modified times of this file. """ -+ os.utime(self, times) -+ -+ def chmod(self, mode): -+ os.chmod(self, mode) -+ -+ if hasattr(os, 'chown'): -+ def chown(self, uid, gid): -+ os.chown(self, uid, gid) -+ -+ def rename(self, new): -+ os.rename(self, new) -+ -+ def renames(self, new): -+ os.renames(self, new) -+ -+ -+ # --- Create/delete operations on directories -+ -+ def mkdir(self, mode=0777): -+ os.mkdir(self, mode) -+ -+ def makedirs(self, mode=0777): -+ os.makedirs(self, mode) -+ -+ def rmdir(self): -+ os.rmdir(self) -+ -+ def removedirs(self): -+ os.removedirs(self) -+ -+ -+ # --- Modifying operations on files -+ -+ def touch(self): -+ """ Set the access/modified times of this file to the current time. -+ Create the file if it does not exist. -+ """ -+ fd = os.open(self, os.O_WRONLY | os.O_CREAT, 0666) -+ os.close(fd) -+ os.utime(self, None) -+ -+ def remove(self): -+ os.remove(self) -+ -+ def unlink(self): -+ os.unlink(self) -+ -+ -+ # --- Links -+ -+ if hasattr(os, 'link'): -+ def link(self, newpath): -+ """ Create a hard link at 'newpath', pointing to this file. """ -+ os.link(self, newpath) -+ -+ if hasattr(os, 'symlink'): -+ def symlink(self, newlink): -+ """ Create a symbolic link at 'newlink', pointing here. """ -+ os.symlink(self, newlink) -+ -+ if hasattr(os, 'readlink'): -+ def readlink(self): -+ """ Return the path to which this symbolic link points. -+ -+ The result may be an absolute or a relative path. -+ """ -+ return self.__class__(os.readlink(self)) -+ -+ def readlinkabs(self): -+ """ Return the path to which this symbolic link points. -+ -+ The result is always an absolute path. -+ """ -+ p = self.readlink() -+ if p.isabs(): -+ return p -+ else: -+ return (self.parent / p).abspath() -+ -+ -+ # --- High-level functions from shutil -+ -+ copyfile = shutil.copyfile -+ copymode = shutil.copymode -+ copystat = shutil.copystat -+ copy = shutil.copy -+ copy2 = shutil.copy2 -+ copytree = shutil.copytree -+ if hasattr(shutil, 'move'): -+ move = shutil.move -+ rmtree = shutil.rmtree -+ -+ -+ # --- Special stuff from os -+ -+ if hasattr(os, 'chroot'): -+ def chroot(self): -+ os.chroot(self) -+ -+ if hasattr(os, 'startfile'): -+ def startfile(self): -+ os.startfile(self) -+ -Index: ipython-0.10/IPython/external/path.py -=================================================================== ---- ipython-0.10.orig/IPython/external/path.py -+++ /dev/null -@@ -1,973 +0,0 @@ --""" path.py - An object representing a path to a file or directory. -- --Example: -- --from IPython.external.path import path --d = path('/home/guido/bin') --for f in d.files('*.py'): -- f.chmod(0755) -- --This module requires Python 2.2 or later. -- -- --URL: http://www.jorendorff.com/articles/python/path --Author: Jason Orendorff (and others - see the url!) --Date: 9 Mar 2007 --""" -- -- --# TODO --# - Tree-walking functions don't avoid symlink loops. Matt Harrison --# sent me a patch for this. --# - Bug in write_text(). It doesn't support Universal newline mode. --# - Better error message in listdir() when self isn't a --# directory. (On Windows, the error message really sucks.) --# - Make sure everything has a good docstring. --# - Add methods for regex find and replace. --# - guess_content_type() method? --# - Perhaps support arguments to touch(). -- --from __future__ import generators -- --import sys, warnings, os, fnmatch, glob, shutil, codecs --# deprecated in python 2.6 --warnings.filterwarnings('ignore', r'.*md5.*') --import md5 -- --__version__ = '2.2' --__all__ = ['path'] -- --# Platform-specific support for path.owner --if os.name == 'nt': -- try: -- import win32security -- except ImportError: -- win32security = None --else: -- try: -- import pwd -- except ImportError: -- pwd = None -- --# Pre-2.3 support. Are unicode filenames supported? --_base = str --_getcwd = os.getcwd --try: -- if os.path.supports_unicode_filenames: -- _base = unicode -- _getcwd = os.getcwdu --except AttributeError: -- pass -- --# Pre-2.3 workaround for booleans --try: -- True, False --except NameError: -- True, False = 1, 0 -- --# Pre-2.3 workaround for basestring. --try: -- basestring --except NameError: -- basestring = (str, unicode) -- --# Universal newline support --_textmode = 'r' --if hasattr(file, 'newlines'): -- _textmode = 'U' -- -- --class TreeWalkWarning(Warning): -- pass -- --class path(_base): -- """ Represents a filesystem path. -- -- For documentation on individual methods, consult their -- counterparts in os.path. -- """ -- -- # --- Special Python methods. -- -- def __repr__(self): -- return 'path(%s)' % _base.__repr__(self) -- -- # Adding a path and a string yields a path. -- def __add__(self, more): -- try: -- resultStr = _base.__add__(self, more) -- except TypeError: #Python bug -- resultStr = NotImplemented -- if resultStr is NotImplemented: -- return resultStr -- return self.__class__(resultStr) -- -- def __radd__(self, other): -- if isinstance(other, basestring): -- return self.__class__(other.__add__(self)) -- else: -- return NotImplemented -- -- # The / operator joins paths. -- def __div__(self, rel): -- """ fp.__div__(rel) == fp / rel == fp.joinpath(rel) -- -- Join two path components, adding a separator character if -- needed. -- """ -- return self.__class__(os.path.join(self, rel)) -- -- # Make the / operator work even when true division is enabled. -- __truediv__ = __div__ -- -- def getcwd(cls): -- """ Return the current working directory as a path object. """ -- return cls(_getcwd()) -- getcwd = classmethod(getcwd) -- -- -- # --- Operations on path strings. -- -- isabs = os.path.isabs -- def abspath(self): return self.__class__(os.path.abspath(self)) -- def normcase(self): return self.__class__(os.path.normcase(self)) -- def normpath(self): return self.__class__(os.path.normpath(self)) -- def realpath(self): return self.__class__(os.path.realpath(self)) -- def expanduser(self): return self.__class__(os.path.expanduser(self)) -- def expandvars(self): return self.__class__(os.path.expandvars(self)) -- def dirname(self): return self.__class__(os.path.dirname(self)) -- basename = os.path.basename -- -- def expand(self): -- """ Clean up a filename by calling expandvars(), -- expanduser(), and normpath() on it. -- -- This is commonly everything needed to clean up a filename -- read from a configuration file, for example. -- """ -- return self.expandvars().expanduser().normpath() -- -- def _get_namebase(self): -- base, ext = os.path.splitext(self.name) -- return base -- -- def _get_ext(self): -- f, ext = os.path.splitext(_base(self)) -- return ext -- -- def _get_drive(self): -- drive, r = os.path.splitdrive(self) -- return self.__class__(drive) -- -- parent = property( -- dirname, None, None, -- """ This path's parent directory, as a new path object. -- -- For example, path('/usr/local/lib/libpython.so').parent == path('/usr/local/lib') -- """) -- -- name = property( -- basename, None, None, -- """ The name of this file or directory without the full path. -- -- For example, path('/usr/local/lib/libpython.so').name == 'libpython.so' -- """) -- -- namebase = property( -- _get_namebase, None, None, -- """ The same as path.name, but with one file extension stripped off. -- -- For example, path('/home/guido/python.tar.gz').name == 'python.tar.gz', -- but path('/home/guido/python.tar.gz').namebase == 'python.tar' -- """) -- -- ext = property( -- _get_ext, None, None, -- """ The file extension, for example '.py'. """) -- -- drive = property( -- _get_drive, None, None, -- """ The drive specifier, for example 'C:'. -- This is always empty on systems that don't use drive specifiers. -- """) -- -- def splitpath(self): -- """ p.splitpath() -> Return (p.parent, p.name). """ -- parent, child = os.path.split(self) -- return self.__class__(parent), child -- -- def splitdrive(self): -- """ p.splitdrive() -> Return (p.drive, ). -- -- Split the drive specifier from this path. If there is -- no drive specifier, p.drive is empty, so the return value -- is simply (path(''), p). This is always the case on Unix. -- """ -- drive, rel = os.path.splitdrive(self) -- return self.__class__(drive), rel -- -- def splitext(self): -- """ p.splitext() -> Return (p.stripext(), p.ext). -- -- Split the filename extension from this path and return -- the two parts. Either part may be empty. -- -- The extension is everything from '.' to the end of the -- last path segment. This has the property that if -- (a, b) == p.splitext(), then a + b == p. -- """ -- filename, ext = os.path.splitext(self) -- return self.__class__(filename), ext -- -- def stripext(self): -- """ p.stripext() -> Remove one file extension from the path. -- -- For example, path('/home/guido/python.tar.gz').stripext() -- returns path('/home/guido/python.tar'). -- """ -- return self.splitext()[0] -- -- if hasattr(os.path, 'splitunc'): -- def splitunc(self): -- unc, rest = os.path.splitunc(self) -- return self.__class__(unc), rest -- -- def _get_uncshare(self): -- unc, r = os.path.splitunc(self) -- return self.__class__(unc) -- -- uncshare = property( -- _get_uncshare, None, None, -- """ The UNC mount point for this path. -- This is empty for paths on local drives. """) -- -- def joinpath(self, *args): -- """ Join two or more path components, adding a separator -- character (os.sep) if needed. Returns a new path -- object. -- """ -- return self.__class__(os.path.join(self, *args)) -- -- def splitall(self): -- r""" Return a list of the path components in this path. -- -- The first item in the list will be a path. Its value will be -- either os.curdir, os.pardir, empty, or the root directory of -- this path (for example, '/' or 'C:\\'). The other items in -- the list will be strings. -- -- path.path.joinpath(*result) will yield the original path. -- """ -- parts = [] -- loc = self -- while loc != os.curdir and loc != os.pardir: -- prev = loc -- loc, child = prev.splitpath() -- if loc == prev: -- break -- parts.append(child) -- parts.append(loc) -- parts.reverse() -- return parts -- -- def relpath(self): -- """ Return this path as a relative path, -- based from the current working directory. -- """ -- cwd = self.__class__(os.getcwd()) -- return cwd.relpathto(self) -- -- def relpathto(self, dest): -- """ Return a relative path from self to dest. -- -- If there is no relative path from self to dest, for example if -- they reside on different drives in Windows, then this returns -- dest.abspath(). -- """ -- origin = self.abspath() -- dest = self.__class__(dest).abspath() -- -- orig_list = origin.normcase().splitall() -- # Don't normcase dest! We want to preserve the case. -- dest_list = dest.splitall() -- -- if orig_list[0] != os.path.normcase(dest_list[0]): -- # Can't get here from there. -- return dest -- -- # Find the location where the two paths start to differ. -- i = 0 -- for start_seg, dest_seg in zip(orig_list, dest_list): -- if start_seg != os.path.normcase(dest_seg): -- break -- i += 1 -- -- # Now i is the point where the two paths diverge. -- # Need a certain number of "os.pardir"s to work up -- # from the origin to the point of divergence. -- segments = [os.pardir] * (len(orig_list) - i) -- # Need to add the diverging part of dest_list. -- segments += dest_list[i:] -- if len(segments) == 0: -- # If they happen to be identical, use os.curdir. -- relpath = os.curdir -- else: -- relpath = os.path.join(*segments) -- return self.__class__(relpath) -- -- # --- Listing, searching, walking, and matching -- -- def listdir(self, pattern=None): -- """ D.listdir() -> List of items in this directory. -- -- Use D.files() or D.dirs() instead if you want a listing -- of just files or just subdirectories. -- -- The elements of the list are path objects. -- -- With the optional 'pattern' argument, this only lists -- items whose names match the given pattern. -- """ -- names = os.listdir(self) -- if pattern is not None: -- names = fnmatch.filter(names, pattern) -- return [self / child for child in names] -- -- def dirs(self, pattern=None): -- """ D.dirs() -> List of this directory's subdirectories. -- -- The elements of the list are path objects. -- This does not walk recursively into subdirectories -- (but see path.walkdirs). -- -- With the optional 'pattern' argument, this only lists -- directories whose names match the given pattern. For -- example, d.dirs('build-*'). -- """ -- return [p for p in self.listdir(pattern) if p.isdir()] -- -- def files(self, pattern=None): -- """ D.files() -> List of the files in this directory. -- -- The elements of the list are path objects. -- This does not walk into subdirectories (see path.walkfiles). -- -- With the optional 'pattern' argument, this only lists files -- whose names match the given pattern. For example, -- d.files('*.pyc'). -- """ -- -- return [p for p in self.listdir(pattern) if p.isfile()] -- -- def walk(self, pattern=None, errors='strict'): -- """ D.walk() -> iterator over files and subdirs, recursively. -- -- The iterator yields path objects naming each child item of -- this directory and its descendants. This requires that -- D.isdir(). -- -- This performs a depth-first traversal of the directory tree. -- Each directory is returned just before all its children. -- -- The errors= keyword argument controls behavior when an -- error occurs. The default is 'strict', which causes an -- exception. The other allowed values are 'warn', which -- reports the error via warnings.warn(), and 'ignore'. -- """ -- if errors not in ('strict', 'warn', 'ignore'): -- raise ValueError("invalid errors parameter") -- -- try: -- childList = self.listdir() -- except Exception: -- if errors == 'ignore': -- return -- elif errors == 'warn': -- warnings.warn( -- "Unable to list directory '%s': %s" -- % (self, sys.exc_info()[1]), -- TreeWalkWarning) -- return -- else: -- raise -- -- for child in childList: -- if pattern is None or child.fnmatch(pattern): -- yield child -- try: -- isdir = child.isdir() -- except Exception: -- if errors == 'ignore': -- isdir = False -- elif errors == 'warn': -- warnings.warn( -- "Unable to access '%s': %s" -- % (child, sys.exc_info()[1]), -- TreeWalkWarning) -- isdir = False -- else: -- raise -- -- if isdir: -- for item in child.walk(pattern, errors): -- yield item -- -- def walkdirs(self, pattern=None, errors='strict'): -- """ D.walkdirs() -> iterator over subdirs, recursively. -- -- With the optional 'pattern' argument, this yields only -- directories whose names match the given pattern. For -- example, mydir.walkdirs('*test') yields only directories -- with names ending in 'test'. -- -- The errors= keyword argument controls behavior when an -- error occurs. The default is 'strict', which causes an -- exception. The other allowed values are 'warn', which -- reports the error via warnings.warn(), and 'ignore'. -- """ -- if errors not in ('strict', 'warn', 'ignore'): -- raise ValueError("invalid errors parameter") -- -- try: -- dirs = self.dirs() -- except Exception: -- if errors == 'ignore': -- return -- elif errors == 'warn': -- warnings.warn( -- "Unable to list directory '%s': %s" -- % (self, sys.exc_info()[1]), -- TreeWalkWarning) -- return -- else: -- raise -- -- for child in dirs: -- if pattern is None or child.fnmatch(pattern): -- yield child -- for subsubdir in child.walkdirs(pattern, errors): -- yield subsubdir -- -- def walkfiles(self, pattern=None, errors='strict'): -- """ D.walkfiles() -> iterator over files in D, recursively. -- -- The optional argument, pattern, limits the results to files -- with names that match the pattern. For example, -- mydir.walkfiles('*.tmp') yields only files with the .tmp -- extension. -- """ -- if errors not in ('strict', 'warn', 'ignore'): -- raise ValueError("invalid errors parameter") -- -- try: -- childList = self.listdir() -- except Exception: -- if errors == 'ignore': -- return -- elif errors == 'warn': -- warnings.warn( -- "Unable to list directory '%s': %s" -- % (self, sys.exc_info()[1]), -- TreeWalkWarning) -- return -- else: -- raise -- -- for child in childList: -- try: -- isfile = child.isfile() -- isdir = not isfile and child.isdir() -- except: -- if errors == 'ignore': -- continue -- elif errors == 'warn': -- warnings.warn( -- "Unable to access '%s': %s" -- % (self, sys.exc_info()[1]), -- TreeWalkWarning) -- continue -- else: -- raise -- -- if isfile: -- if pattern is None or child.fnmatch(pattern): -- yield child -- elif isdir: -- for f in child.walkfiles(pattern, errors): -- yield f -- -- def fnmatch(self, pattern): -- """ Return True if self.name matches the given pattern. -- -- pattern - A filename pattern with wildcards, -- for example '*.py'. -- """ -- return fnmatch.fnmatch(self.name, pattern) -- -- def glob(self, pattern): -- """ Return a list of path objects that match the pattern. -- -- pattern - a path relative to this directory, with wildcards. -- -- For example, path('/users').glob('*/bin/*') returns a list -- of all the files users have in their bin directories. -- """ -- cls = self.__class__ -- return [cls(s) for s in glob.glob(_base(self / pattern))] -- -- -- # --- Reading or writing an entire file at once. -- -- def open(self, mode='r'): -- """ Open this file. Return a file object. """ -- return file(self, mode) -- -- def bytes(self): -- """ Open this file, read all bytes, return them as a string. """ -- f = self.open('rb') -- try: -- return f.read() -- finally: -- f.close() -- -- def write_bytes(self, bytes, append=False): -- """ Open this file and write the given bytes to it. -- -- Default behavior is to overwrite any existing file. -- Call p.write_bytes(bytes, append=True) to append instead. -- """ -- if append: -- mode = 'ab' -- else: -- mode = 'wb' -- f = self.open(mode) -- try: -- f.write(bytes) -- finally: -- f.close() -- -- def text(self, encoding=None, errors='strict'): -- r""" Open this file, read it in, return the content as a string. -- -- This uses 'U' mode in Python 2.3 and later, so '\r\n' and '\r' -- are automatically translated to '\n'. -- -- Optional arguments: -- -- encoding - The Unicode encoding (or character set) of -- the file. If present, the content of the file is -- decoded and returned as a unicode object; otherwise -- it is returned as an 8-bit str. -- errors - How to handle Unicode errors; see help(str.decode) -- for the options. Default is 'strict'. -- """ -- if encoding is None: -- # 8-bit -- f = self.open(_textmode) -- try: -- return f.read() -- finally: -- f.close() -- else: -- # Unicode -- f = codecs.open(self, 'r', encoding, errors) -- # (Note - Can't use 'U' mode here, since codecs.open -- # doesn't support 'U' mode, even in Python 2.3.) -- try: -- t = f.read() -- finally: -- f.close() -- return (t.replace(u'\r\n', u'\n') -- .replace(u'\r\x85', u'\n') -- .replace(u'\r', u'\n') -- .replace(u'\x85', u'\n') -- .replace(u'\u2028', u'\n')) -- -- def write_text(self, text, encoding=None, errors='strict', linesep=os.linesep, append=False): -- r""" Write the given text to this file. -- -- The default behavior is to overwrite any existing file; -- to append instead, use the 'append=True' keyword argument. -- -- There are two differences between path.write_text() and -- path.write_bytes(): newline handling and Unicode handling. -- See below. -- -- Parameters: -- -- - text - str/unicode - The text to be written. -- -- - encoding - str - The Unicode encoding that will be used. -- This is ignored if 'text' isn't a Unicode string. -- -- - errors - str - How to handle Unicode encoding errors. -- Default is 'strict'. See help(unicode.encode) for the -- options. This is ignored if 'text' isn't a Unicode -- string. -- -- - linesep - keyword argument - str/unicode - The sequence of -- characters to be used to mark end-of-line. The default is -- os.linesep. You can also specify None; this means to -- leave all newlines as they are in 'text'. -- -- - append - keyword argument - bool - Specifies what to do if -- the file already exists (True: append to the end of it; -- False: overwrite it.) The default is False. -- -- -- --- Newline handling. -- -- write_text() converts all standard end-of-line sequences -- ('\n', '\r', and '\r\n') to your platform's default end-of-line -- sequence (see os.linesep; on Windows, for example, the -- end-of-line marker is '\r\n'). -- -- If you don't like your platform's default, you can override it -- using the 'linesep=' keyword argument. If you specifically want -- write_text() to preserve the newlines as-is, use 'linesep=None'. -- -- This applies to Unicode text the same as to 8-bit text, except -- there are three additional standard Unicode end-of-line sequences: -- u'\x85', u'\r\x85', and u'\u2028'. -- -- (This is slightly different from when you open a file for -- writing with fopen(filename, "w") in C or file(filename, 'w') -- in Python.) -- -- -- --- Unicode -- -- If 'text' isn't Unicode, then apart from newline handling, the -- bytes are written verbatim to the file. The 'encoding' and -- 'errors' arguments are not used and must be omitted. -- -- If 'text' is Unicode, it is first converted to bytes using the -- specified 'encoding' (or the default encoding if 'encoding' -- isn't specified). The 'errors' argument applies only to this -- conversion. -- -- """ -- if isinstance(text, unicode): -- if linesep is not None: -- # Convert all standard end-of-line sequences to -- # ordinary newline characters. -- text = (text.replace(u'\r\n', u'\n') -- .replace(u'\r\x85', u'\n') -- .replace(u'\r', u'\n') -- .replace(u'\x85', u'\n') -- .replace(u'\u2028', u'\n')) -- text = text.replace(u'\n', linesep) -- if encoding is None: -- encoding = sys.getdefaultencoding() -- bytes = text.encode(encoding, errors) -- else: -- # It is an error to specify an encoding if 'text' is -- # an 8-bit string. -- assert encoding is None -- -- if linesep is not None: -- text = (text.replace('\r\n', '\n') -- .replace('\r', '\n')) -- bytes = text.replace('\n', linesep) -- -- self.write_bytes(bytes, append) -- -- def lines(self, encoding=None, errors='strict', retain=True): -- r""" Open this file, read all lines, return them in a list. -- -- Optional arguments: -- encoding - The Unicode encoding (or character set) of -- the file. The default is None, meaning the content -- of the file is read as 8-bit characters and returned -- as a list of (non-Unicode) str objects. -- errors - How to handle Unicode errors; see help(str.decode) -- for the options. Default is 'strict' -- retain - If true, retain newline characters; but all newline -- character combinations ('\r', '\n', '\r\n') are -- translated to '\n'. If false, newline characters are -- stripped off. Default is True. -- -- This uses 'U' mode in Python 2.3 and later. -- """ -- if encoding is None and retain: -- f = self.open(_textmode) -- try: -- return f.readlines() -- finally: -- f.close() -- else: -- return self.text(encoding, errors).splitlines(retain) -- -- def write_lines(self, lines, encoding=None, errors='strict', -- linesep=os.linesep, append=False): -- r""" Write the given lines of text to this file. -- -- By default this overwrites any existing file at this path. -- -- This puts a platform-specific newline sequence on every line. -- See 'linesep' below. -- -- lines - A list of strings. -- -- encoding - A Unicode encoding to use. This applies only if -- 'lines' contains any Unicode strings. -- -- errors - How to handle errors in Unicode encoding. This -- also applies only to Unicode strings. -- -- linesep - The desired line-ending. This line-ending is -- applied to every line. If a line already has any -- standard line ending ('\r', '\n', '\r\n', u'\x85', -- u'\r\x85', u'\u2028'), that will be stripped off and -- this will be used instead. The default is os.linesep, -- which is platform-dependent ('\r\n' on Windows, '\n' on -- Unix, etc.) Specify None to write the lines as-is, -- like file.writelines(). -- -- Use the keyword argument append=True to append lines to the -- file. The default is to overwrite the file. Warning: -- When you use this with Unicode data, if the encoding of the -- existing data in the file is different from the encoding -- you specify with the encoding= parameter, the result is -- mixed-encoding data, which can really confuse someone trying -- to read the file later. -- """ -- if append: -- mode = 'ab' -- else: -- mode = 'wb' -- f = self.open(mode) -- try: -- for line in lines: -- isUnicode = isinstance(line, unicode) -- if linesep is not None: -- # Strip off any existing line-end and add the -- # specified linesep string. -- if isUnicode: -- if line[-2:] in (u'\r\n', u'\x0d\x85'): -- line = line[:-2] -- elif line[-1:] in (u'\r', u'\n', -- u'\x85', u'\u2028'): -- line = line[:-1] -- else: -- if line[-2:] == '\r\n': -- line = line[:-2] -- elif line[-1:] in ('\r', '\n'): -- line = line[:-1] -- line += linesep -- if isUnicode: -- if encoding is None: -- encoding = sys.getdefaultencoding() -- line = line.encode(encoding, errors) -- f.write(line) -- finally: -- f.close() -- -- def read_md5(self): -- """ Calculate the md5 hash for this file. -- -- This reads through the entire file. -- """ -- f = self.open('rb') -- try: -- m = md5.new() -- while True: -- d = f.read(8192) -- if not d: -- break -- m.update(d) -- finally: -- f.close() -- return m.digest() -- -- # --- Methods for querying the filesystem. -- -- exists = os.path.exists -- isdir = os.path.isdir -- isfile = os.path.isfile -- islink = os.path.islink -- ismount = os.path.ismount -- -- if hasattr(os.path, 'samefile'): -- samefile = os.path.samefile -- -- getatime = os.path.getatime -- atime = property( -- getatime, None, None, -- """ Last access time of the file. """) -- -- getmtime = os.path.getmtime -- mtime = property( -- getmtime, None, None, -- """ Last-modified time of the file. """) -- -- if hasattr(os.path, 'getctime'): -- getctime = os.path.getctime -- ctime = property( -- getctime, None, None, -- """ Creation time of the file. """) -- -- getsize = os.path.getsize -- size = property( -- getsize, None, None, -- """ Size of the file, in bytes. """) -- -- if hasattr(os, 'access'): -- def access(self, mode): -- """ Return true if current user has access to this path. -- -- mode - One of the constants os.F_OK, os.R_OK, os.W_OK, os.X_OK -- """ -- return os.access(self, mode) -- -- def stat(self): -- """ Perform a stat() system call on this path. """ -- return os.stat(self) -- -- def lstat(self): -- """ Like path.stat(), but do not follow symbolic links. """ -- return os.lstat(self) -- -- def get_owner(self): -- r""" Return the name of the owner of this file or directory. -- -- This follows symbolic links. -- -- On Windows, this returns a name of the form ur'DOMAIN\User Name'. -- On Windows, a group can own a file or directory. -- """ -- if os.name == 'nt': -- if win32security is None: -- raise Exception("path.owner requires win32all to be installed") -- desc = win32security.GetFileSecurity( -- self, win32security.OWNER_SECURITY_INFORMATION) -- sid = desc.GetSecurityDescriptorOwner() -- account, domain, typecode = win32security.LookupAccountSid(None, sid) -- return domain + u'\\' + account -- else: -- if pwd is None: -- raise NotImplementedError("path.owner is not implemented on this platform.") -- st = self.stat() -- return pwd.getpwuid(st.st_uid).pw_name -- -- owner = property( -- get_owner, None, None, -- """ Name of the owner of this file or directory. """) -- -- if hasattr(os, 'statvfs'): -- def statvfs(self): -- """ Perform a statvfs() system call on this path. """ -- return os.statvfs(self) -- -- if hasattr(os, 'pathconf'): -- def pathconf(self, name): -- return os.pathconf(self, name) -- -- -- # --- Modifying operations on files and directories -- -- def utime(self, times): -- """ Set the access and modified times of this file. """ -- os.utime(self, times) -- -- def chmod(self, mode): -- os.chmod(self, mode) -- -- if hasattr(os, 'chown'): -- def chown(self, uid, gid): -- os.chown(self, uid, gid) -- -- def rename(self, new): -- os.rename(self, new) -- -- def renames(self, new): -- os.renames(self, new) -- -- -- # --- Create/delete operations on directories -- -- def mkdir(self, mode=0777): -- os.mkdir(self, mode) -- -- def makedirs(self, mode=0777): -- os.makedirs(self, mode) -- -- def rmdir(self): -- os.rmdir(self) -- -- def removedirs(self): -- os.removedirs(self) -- -- -- # --- Modifying operations on files -- -- def touch(self): -- """ Set the access/modified times of this file to the current time. -- Create the file if it does not exist. -- """ -- fd = os.open(self, os.O_WRONLY | os.O_CREAT, 0666) -- os.close(fd) -- os.utime(self, None) -- -- def remove(self): -- os.remove(self) -- -- def unlink(self): -- os.unlink(self) -- -- -- # --- Links -- -- if hasattr(os, 'link'): -- def link(self, newpath): -- """ Create a hard link at 'newpath', pointing to this file. """ -- os.link(self, newpath) -- -- if hasattr(os, 'symlink'): -- def symlink(self, newlink): -- """ Create a symbolic link at 'newlink', pointing here. """ -- os.symlink(self, newlink) -- -- if hasattr(os, 'readlink'): -- def readlink(self): -- """ Return the path to which this symbolic link points. -- -- The result may be an absolute or a relative path. -- """ -- return self.__class__(os.readlink(self)) -- -- def readlinkabs(self): -- """ Return the path to which this symbolic link points. -- -- The result is always an absolute path. -- """ -- p = self.readlink() -- if p.isabs(): -- return p -- else: -- return (self.parent / p).abspath() -- -- -- # --- High-level functions from shutil -- -- copyfile = shutil.copyfile -- copymode = shutil.copymode -- copystat = shutil.copystat -- copy = shutil.copy -- copy2 = shutil.copy2 -- copytree = shutil.copytree -- if hasattr(shutil, 'move'): -- move = shutil.move -- rmtree = shutil.rmtree -- -- -- # --- Special stuff from os -- -- if hasattr(os, 'chroot'): -- def chroot(self): -- os.chroot(self) -- -- if hasattr(os, 'startfile'): -- def startfile(self): -- os.startfile(self) -- Index: ipython-0.10/IPython/external/pretty/__init__.py =================================================================== --- /dev/null @@ -12858,1426 +68,6 @@ Index: ipython-0.10/IPython/external/pretty/__init__.py + from pretty import * +except ImportError: + from _pretty import * -Index: ipython-0.10/IPython/external/pretty/_pretty.py -=================================================================== ---- /dev/null -+++ ipython-0.10/IPython/external/pretty/_pretty.py -@@ -0,0 +1,705 @@ -+# -*- coding: utf-8 -*- -+""" -+ pretty -+ ~~ -+ -+ Python advanced pretty printer. This pretty printer is intended to -+ replace the old `pprint` python module which does not allow developers -+ to provide their own pretty print callbacks. -+ -+ This module is based on ruby's `prettyprint.rb` library by `Tanaka Akira`. -+ -+ -+ Example Usage -+ ============= -+ -+ To directly print the representation of an object use `pprint`:: -+ -+ from pretty import pprint -+ pprint(complex_object) -+ -+ To get a string of the output use `pretty`:: -+ -+ from pretty import pretty -+ string = pretty(complex_object) -+ -+ -+ Extending -+ ========= -+ -+ The pretty library allows developers to add pretty printing rules for their -+ own objects. This process is straightforward. All you have to do is to -+ add a `__pretty__` method to your object and call the methods on the -+ pretty printer passed:: -+ -+ class MyObject(object): -+ -+ def __pretty__(self, p, cycle): -+ ... -+ -+ Depending on the python version you want to support you have two -+ possibilities. The following list shows the python 2.5 version and the -+ compatibility one. -+ -+ -+ Here the example implementation of a `__pretty__` method for a list -+ subclass for python 2.5 and higher (python 2.5 requires the with statement -+ __future__ import):: -+ -+ class MyList(list): -+ -+ def __pretty__(self, p, cycle): -+ if cycle: -+ p.text('MyList(...)') -+ else: -+ with p.group(8, 'MyList([', '])'): -+ for idx, item in enumerate(self): -+ if idx: -+ p.text(',') -+ p.breakable() -+ p.pretty(item) -+ -+ The `cycle` parameter is `True` if pretty detected a cycle. You *have* to -+ react to that or the result is an infinite loop. `p.text()` just adds -+ non breaking text to the output, `p.breakable()` either adds a whitespace -+ or breaks here. If you pass it an argument it's used instead of the -+ default space. `p.pretty` prettyprints another object using the pretty print -+ method. -+ -+ The first parameter to the `group` function specifies the extra indentation -+ of the next line. In this example the next item will either be not -+ breaked (if the items are short enough) or aligned with the right edge of -+ the opening bracked of `MyList`. -+ -+ If you want to support python 2.4 and lower you can use this code:: -+ -+ class MyList(list): -+ -+ def __pretty__(self, p, cycle): -+ if cycle: -+ p.text('MyList(...)') -+ else: -+ p.begin_group(8, 'MyList([') -+ for idx, item in enumerate(self): -+ if idx: -+ p.text(',') -+ p.breakable() -+ p.pretty(item) -+ p.end_group(8, '])') -+ -+ If you just want to indent something you can use the group function -+ without open / close parameters. Under python 2.5 you can also use this -+ code:: -+ -+ with p.indent(2): -+ ... -+ -+ Or under python2.4 you might want to modify ``p.indentation`` by hand but -+ this is rather ugly. -+ -+ :copyright: 2007 by Armin Ronacher. -+ Portions (c) 2009 by Robert Kern. -+ :license: BSD License. -+""" -+import __future__ -+import sys -+import types -+import re -+import datetime -+from StringIO import StringIO -+from collections import deque -+ -+ -+__all__ = ['pretty', 'pprint', 'PrettyPrinter', 'RepresentationPrinter', -+ 'for_type', 'for_type_by_name'] -+ -+ -+_re_pattern_type = type(re.compile('')) -+ -+ -+def pretty(obj, verbose=False, max_width=79, newline='\n'): -+ """ -+ Pretty print the object's representation. -+ """ -+ stream = StringIO() -+ printer = RepresentationPrinter(stream, verbose, max_width, newline) -+ printer.pretty(obj) -+ printer.flush() -+ return stream.getvalue() -+ -+ -+def pprint(obj, verbose=False, max_width=79, newline='\n'): -+ """ -+ Like `pretty` but print to stdout. -+ """ -+ printer = RepresentationPrinter(sys.stdout, verbose, max_width, newline) -+ printer.pretty(obj) -+ printer.flush() -+ sys.stdout.write(newline) -+ sys.stdout.flush() -+ -+ -+# add python2.5 context managers if we have the with statement feature -+if hasattr(__future__, 'with_statement'): exec ''' -+from __future__ import with_statement -+from contextlib import contextmanager -+ -+class _PrettyPrinterBase(object): -+ -+ @contextmanager -+ def indent(self, indent): -+ """with statement support for indenting/dedenting.""" -+ self.indentation += indent -+ try: -+ yield -+ finally: -+ self.indentation -= indent -+ -+ @contextmanager -+ def group(self, indent=0, open='', close=''): -+ """like begin_group / end_group but for the with statement.""" -+ self.begin_group(indent, open) -+ try: -+ with self.indent(indent): -+ yield -+ finally: -+ self.end_group(indent, close) -+''' -+else: -+ class _PrettyPrinterBase(object): -+ -+ def _unsupported(self, *a, **kw): -+ """unsupported operation""" -+ raise RuntimeError('not available in this python version') -+ group = indent = _unsupported -+ del _unsupported -+ -+ -+class PrettyPrinter(_PrettyPrinterBase): -+ """ -+ Baseclass for the `RepresentationPrinter` prettyprinter that is used to -+ generate pretty reprs of objects. Contrary to the `RepresentationPrinter` -+ this printer knows nothing about the default pprinters or the `__pretty__` -+ callback method. -+ """ -+ -+ def __init__(self, output, max_width=79, newline='\n'): -+ self.output = output -+ self.max_width = max_width -+ self.newline = newline -+ self.output_width = 0 -+ self.buffer_width = 0 -+ self.buffer = deque() -+ -+ root_group = Group(0) -+ self.group_stack = [root_group] -+ self.group_queue = GroupQueue(root_group) -+ self.indentation = 0 -+ -+ def _break_outer_groups(self): -+ while self.max_width < self.output_width + self.buffer_width: -+ group = self.group_queue.deq() -+ if not group: -+ return -+ while group.breakables: -+ x = self.buffer.popleft() -+ self.output_width = x.output(self.output, self.output_width) -+ self.buffer_width -= x.width -+ while self.buffer and isinstance(self.buffer[0], Text): -+ x = self.buffer.popleft() -+ self.output_width = x.output(self.output, self.output_width) -+ self.buffer_width -= x.width -+ -+ def text(self, obj): -+ """Add literal text to the output.""" -+ width = len(obj) -+ if self.buffer: -+ text = self.buffer[-1] -+ if not isinstance(text, Text): -+ text = Text() -+ self.buffer.append(text) -+ text.add(obj, width) -+ self.buffer_width += width -+ self._break_outer_groups() -+ else: -+ self.output.write(obj) -+ self.output_width += width -+ -+ def breakable(self, sep=' '): -+ """ -+ Add a breakable separator to the output. This does not mean that it -+ will automatically break here. If no breaking on this position takes -+ place the `sep` is inserted which default to one space. -+ """ -+ width = len(sep) -+ group = self.group_stack[-1] -+ if group.want_break: -+ self.flush() -+ self.output.write(self.newline) -+ self.output.write(' ' * self.indentation) -+ self.output_width = self.indentation -+ self.buffer_width = 0 -+ else: -+ self.buffer.append(Breakable(sep, width, self)) -+ self.buffer_width += width -+ self._break_outer_groups() -+ -+ -+ def begin_group(self, indent=0, open=''): -+ """ -+ Begin a group. If you want support for python < 2.5 which doesn't has -+ the with statement this is the preferred way: -+ -+ p.begin_group(1, '{') -+ ... -+ p.end_group(1, '}') -+ -+ The python 2.5 expression would be this: -+ -+ with p.group(1, '{', '}'): -+ ... -+ -+ The first parameter specifies the indentation for the next line (usually -+ the width of the opening text), the second the opening text. All -+ parameters are optional. -+ """ -+ if open: -+ self.text(open) -+ group = Group(self.group_stack[-1].depth + 1) -+ self.group_stack.append(group) -+ self.group_queue.enq(group) -+ self.indentation += indent -+ -+ def end_group(self, dedent=0, close=''): -+ """End a group. See `begin_group` for more details.""" -+ self.indentation -= dedent -+ group = self.group_stack.pop() -+ if not group.breakables: -+ self.group_queue.remove(group) -+ if close: -+ self.text(close) -+ -+ def flush(self): -+ """Flush data that is left in the buffer.""" -+ for data in self.buffer: -+ self.output_width += data.output(self.output, self.output_width) -+ self.buffer.clear() -+ self.buffer_width = 0 -+ -+ -+def _get_mro(obj_class): -+ """ Get a reasonable method resolution order of a class and its superclasses -+ for both old-style and new-style classes. -+ """ -+ if not hasattr(obj_class, '__mro__'): -+ # Old-style class. Mix in object to make a fake new-style class. -+ try: -+ obj_class = type(obj_class.__name__, (obj_class, object), {}) -+ except TypeError: -+ # Old-style extension type that does not descend from object. -+ # FIXME: try to construct a more thorough MRO. -+ mro = [obj_class] -+ else: -+ mro = obj_class.__mro__[1:-1] -+ else: -+ mro = obj_class.__mro__ -+ return mro -+ -+ -+class RepresentationPrinter(PrettyPrinter): -+ """ -+ Special pretty printer that has a `pretty` method that calls the pretty -+ printer for a python object. -+ -+ This class stores processing data on `self` so you must *never* use -+ this class in a threaded environment. Always lock it or reinstanciate -+ it. -+ -+ Instances also have a verbose flag callbacks can access to control their -+ output. For example the default instance repr prints all attributes and -+ methods that are not prefixed by an underscore if the printer is in -+ verbose mode. -+ """ -+ -+ def __init__(self, output, verbose=False, max_width=79, newline='\n'): -+ PrettyPrinter.__init__(self, output, max_width, newline) -+ self.verbose = verbose -+ self.stack = [] -+ -+ def pretty(self, obj): -+ """Pretty print the given object.""" -+ obj_id = id(obj) -+ cycle = obj_id in self.stack -+ self.stack.append(obj_id) -+ self.begin_group() -+ try: -+ obj_class = getattr(obj, '__class__', None) or type(obj) -+ if hasattr(obj_class, '__pretty__'): -+ return obj_class.__pretty__(obj, self, cycle) -+ try: -+ printer = _singleton_pprinters[obj_id] -+ except (TypeError, KeyError): -+ pass -+ else: -+ return printer(obj, self, cycle) -+ for cls in _get_mro(obj_class): -+ if cls in _type_pprinters: -+ return _type_pprinters[cls](obj, self, cycle) -+ else: -+ printer = self._in_deferred_types(cls) -+ if printer is not None: -+ return printer(obj, self, cycle) -+ return _default_pprint(obj, self, cycle) -+ finally: -+ self.end_group() -+ self.stack.pop() -+ -+ def _in_deferred_types(self, cls): -+ """ -+ Check if the given class is specified in the deferred type registry. -+ -+ Returns the printer from the registry if it exists, and None if the -+ class is not in the registry. Successful matches will be moved to the -+ regular type registry for future use. -+ """ -+ mod = getattr(cls, '__module__', None) -+ name = getattr(cls, '__name__', None) -+ key = (mod, name) -+ printer = None -+ if key in _deferred_type_pprinters: -+ # Move the printer over to the regular registry. -+ printer = _deferred_type_pprinters.pop(key) -+ _type_pprinters[cls] = printer -+ return printer -+ -+ -+ -+class Printable(object): -+ -+ def output(self, stream, output_width): -+ return output_width -+ -+ -+class Text(Printable): -+ -+ def __init__(self): -+ self.objs = [] -+ self.width = 0 -+ -+ def output(self, stream, output_width): -+ for obj in self.objs: -+ stream.write(obj) -+ return output_width + self.width -+ -+ def add(self, obj, width): -+ self.objs.append(obj) -+ self.width += width -+ -+ -+class Breakable(Printable): -+ -+ def __init__(self, seq, width, pretty): -+ self.obj = seq -+ self.width = width -+ self.pretty = pretty -+ self.indentation = pretty.indentation -+ self.group = pretty.group_stack[-1] -+ self.group.breakables.append(self) -+ -+ def output(self, stream, output_width): -+ self.group.breakables.popleft() -+ if self.group.want_break: -+ stream.write(self.pretty.newline) -+ stream.write(' ' * self.indentation) -+ return self.indentation -+ if not self.group.breakables: -+ self.pretty.group_queue.remove(self.group) -+ stream.write(self.obj) -+ return output_width + self.width -+ -+ -+class Group(Printable): -+ -+ def __init__(self, depth): -+ self.depth = depth -+ self.breakables = deque() -+ self.want_break = False -+ -+ -+class GroupQueue(object): -+ -+ def __init__(self, *groups): -+ self.queue = [] -+ for group in groups: -+ self.enq(group) -+ -+ def enq(self, group): -+ depth = group.depth -+ while depth > len(self.queue) - 1: -+ self.queue.append([]) -+ self.queue[depth].append(group) -+ -+ def deq(self): -+ for stack in self.queue: -+ for idx, group in enumerate(reversed(stack)): -+ if group.breakables: -+ del stack[idx] -+ group.want_break = True -+ return group -+ for group in stack: -+ group.want_break = True -+ del stack[:] -+ -+ def remove(self, group): -+ try: -+ self.queue[group.depth].remove(group) -+ except ValueError: -+ pass -+ -+ -+_baseclass_reprs = (object.__repr__, types.InstanceType.__repr__) -+ -+ -+def _default_pprint(obj, p, cycle): -+ """ -+ The default print function. Used if an object does not provide one and -+ it's none of the builtin objects. -+ """ -+ klass = getattr(obj, '__class__', None) or type(obj) -+ if getattr(klass, '__repr__', None) not in _baseclass_reprs: -+ # A user-provided repr. -+ p.text(repr(obj)) -+ return -+ p.begin_group(1, '<') -+ p.pretty(klass) -+ p.text(' at 0x%x' % id(obj)) -+ if cycle: -+ p.text(' ...') -+ elif p.verbose: -+ first = True -+ for key in dir(obj): -+ if not key.startswith('_'): -+ try: -+ value = getattr(obj, key) -+ except AttributeError: -+ continue -+ if isinstance(value, types.MethodType): -+ continue -+ if not first: -+ p.text(',') -+ p.breakable() -+ p.text(key) -+ p.text('=') -+ step = len(key) + 1 -+ p.indentation += step -+ p.pretty(value) -+ p.indentation -= step -+ first = False -+ p.end_group(1, '>') -+ -+ -+def _seq_pprinter_factory(start, end): -+ """ -+ Factory that returns a pprint function useful for sequences. Used by -+ the default pprint for tuples, dicts, lists, sets and frozensets. -+ """ -+ def inner(obj, p, cycle): -+ if cycle: -+ return p.text(start + '...' + end) -+ step = len(start) -+ p.begin_group(step, start) -+ for idx, x in enumerate(obj): -+ if idx: -+ p.text(',') -+ p.breakable() -+ p.pretty(x) -+ if len(obj) == 1 and type(obj) is tuple: -+ # Special case for 1-item tuples. -+ p.text(',') -+ p.end_group(step, end) -+ return inner -+ -+ -+def _dict_pprinter_factory(start, end): -+ """ -+ Factory that returns a pprint function used by the default pprint of -+ dicts and dict proxies. -+ """ -+ def inner(obj, p, cycle): -+ if cycle: -+ return p.text('{...}') -+ p.begin_group(1, start) -+ keys = obj.keys() -+ try: -+ keys.sort() -+ except Exception, e: -+ # Sometimes the keys don't sort. -+ pass -+ for idx, key in enumerate(keys): -+ if idx: -+ p.text(',') -+ p.breakable() -+ p.pretty(key) -+ p.text(': ') -+ p.pretty(obj[key]) -+ p.end_group(1, end) -+ return inner -+ -+ -+def _super_pprint(obj, p, cycle): -+ """The pprint for the super type.""" -+ p.begin_group(8, '') -+ -+ -+def _re_pattern_pprint(obj, p, cycle): -+ """The pprint function for regular expression patterns.""" -+ p.text('re.compile(') -+ pattern = repr(obj.pattern) -+ if pattern[:1] in 'uU': -+ pattern = pattern[1:] -+ prefix = 'ur' -+ else: -+ prefix = 'r' -+ pattern = prefix + pattern.replace('\\\\', '\\') -+ p.text(pattern) -+ if obj.flags: -+ p.text(',') -+ p.breakable() -+ done_one = False -+ for flag in ('TEMPLATE', 'IGNORECASE', 'LOCALE', 'MULTILINE', 'DOTALL', -+ 'UNICODE', 'VERBOSE', 'DEBUG'): -+ if obj.flags & getattr(re, flag): -+ if done_one: -+ p.text('|') -+ p.text('re.' + flag) -+ done_one = True -+ p.text(')') -+ -+ -+def _type_pprint(obj, p, cycle): -+ """The pprint for classes and types.""" -+ if obj.__module__ in ('__builtin__', 'exceptions'): -+ name = obj.__name__ -+ else: -+ name = obj.__module__ + '.' + obj.__name__ -+ p.text(name) -+ -+ -+def _repr_pprint(obj, p, cycle): -+ """A pprint that just redirects to the normal repr function.""" -+ p.text(repr(obj)) -+ -+ -+def _function_pprint(obj, p, cycle): -+ """Base pprint for all functions and builtin functions.""" -+ if obj.__module__ in ('__builtin__', 'exceptions') or not obj.__module__: -+ name = obj.__name__ -+ else: -+ name = obj.__module__ + '.' + obj.__name__ -+ p.text('' % name) -+ -+ -+def _exception_pprint(obj, p, cycle): -+ """Base pprint for all exceptions.""" -+ if obj.__class__.__module__ == 'exceptions': -+ name = obj.__class__.__name__ -+ else: -+ name = '%s.%s' % ( -+ obj.__class__.__module__, -+ obj.__class__.__name__ -+ ) -+ step = len(name) + 1 -+ p.begin_group(step, '(') -+ for idx, arg in enumerate(getattr(obj, 'args', ())): -+ if idx: -+ p.text(',') -+ p.breakable() -+ p.pretty(arg) -+ p.end_group(step, ')') -+ -+ -+#: the exception base -+try: -+ _exception_base = BaseException -+except NameError: -+ _exception_base = Exception -+ -+ -+#: printers for builtin types -+_type_pprinters = { -+ int: _repr_pprint, -+ long: _repr_pprint, -+ float: _repr_pprint, -+ str: _repr_pprint, -+ unicode: _repr_pprint, -+ tuple: _seq_pprinter_factory('(', ')'), -+ list: _seq_pprinter_factory('[', ']'), -+ dict: _dict_pprinter_factory('{', '}'), -+ types.DictProxyType: _dict_pprinter_factory(''), -+ set: _seq_pprinter_factory('set([', '])'), -+ frozenset: _seq_pprinter_factory('frozenset([', '])'), -+ super: _super_pprint, -+ _re_pattern_type: _re_pattern_pprint, -+ type: _type_pprint, -+ types.ClassType: _type_pprint, -+ types.FunctionType: _function_pprint, -+ types.BuiltinFunctionType: _function_pprint, -+ types.SliceType: _repr_pprint, -+ types.MethodType: _repr_pprint, -+ xrange: _repr_pprint, -+ datetime.datetime: _repr_pprint, -+ datetime.timedelta: _repr_pprint, -+ _exception_base: _exception_pprint -+} -+ -+#: printers for types specified by name -+_deferred_type_pprinters = { -+} -+ -+def for_type(typ, func): -+ """ -+ Add a pretty printer for a given type. -+ """ -+ oldfunc = _type_pprinters.get(typ, None) -+ if func is not None: -+ # To support easy restoration of old pprinters, we need to ignore Nones. -+ _type_pprinters[typ] = func -+ return oldfunc -+ -+def for_type_by_name(type_module, type_name, func): -+ """ -+ Add a pretty printer for a type specified by the module and name of a type -+ rather than the type object itself. -+ """ -+ key = (type_module, type_name) -+ oldfunc = _deferred_type_pprinters.get(key, None) -+ if func is not None: -+ # To support easy restoration of old pprinters, we need to ignore Nones. -+ _deferred_type_pprinters[key] = func -+ return oldfunc -+ -+ -+#: printers for the default singletons -+_singleton_pprinters = dict.fromkeys(map(id, [None, True, False, Ellipsis, -+ NotImplemented]), _repr_pprint) -+ -+ -+if __name__ == '__main__': -+ from random import randrange -+ class Foo(object): -+ def __init__(self): -+ self.foo = 1 -+ self.bar = re.compile(r'\s+') -+ self.blub = dict.fromkeys(range(30), randrange(1, 40)) -+ self.hehe = 23424.234234 -+ self.list = ["blub", "blah", self] -+ -+ def get_foo(self): -+ print "foo" -+ -+ pprint(Foo(), verbose=True) -Index: ipython-0.10/IPython/external/pretty.py -=================================================================== ---- ipython-0.10.orig/IPython/external/pretty.py -+++ /dev/null -@@ -1,705 +0,0 @@ --# -*- coding: utf-8 -*- --""" -- pretty -- ~~ -- -- Python advanced pretty printer. This pretty printer is intended to -- replace the old `pprint` python module which does not allow developers -- to provide their own pretty print callbacks. -- -- This module is based on ruby's `prettyprint.rb` library by `Tanaka Akira`. -- -- -- Example Usage -- ============= -- -- To directly print the representation of an object use `pprint`:: -- -- from pretty import pprint -- pprint(complex_object) -- -- To get a string of the output use `pretty`:: -- -- from pretty import pretty -- string = pretty(complex_object) -- -- -- Extending -- ========= -- -- The pretty library allows developers to add pretty printing rules for their -- own objects. This process is straightforward. All you have to do is to -- add a `__pretty__` method to your object and call the methods on the -- pretty printer passed:: -- -- class MyObject(object): -- -- def __pretty__(self, p, cycle): -- ... -- -- Depending on the python version you want to support you have two -- possibilities. The following list shows the python 2.5 version and the -- compatibility one. -- -- -- Here the example implementation of a `__pretty__` method for a list -- subclass for python 2.5 and higher (python 2.5 requires the with statement -- __future__ import):: -- -- class MyList(list): -- -- def __pretty__(self, p, cycle): -- if cycle: -- p.text('MyList(...)') -- else: -- with p.group(8, 'MyList([', '])'): -- for idx, item in enumerate(self): -- if idx: -- p.text(',') -- p.breakable() -- p.pretty(item) -- -- The `cycle` parameter is `True` if pretty detected a cycle. You *have* to -- react to that or the result is an infinite loop. `p.text()` just adds -- non breaking text to the output, `p.breakable()` either adds a whitespace -- or breaks here. If you pass it an argument it's used instead of the -- default space. `p.pretty` prettyprints another object using the pretty print -- method. -- -- The first parameter to the `group` function specifies the extra indentation -- of the next line. In this example the next item will either be not -- breaked (if the items are short enough) or aligned with the right edge of -- the opening bracked of `MyList`. -- -- If you want to support python 2.4 and lower you can use this code:: -- -- class MyList(list): -- -- def __pretty__(self, p, cycle): -- if cycle: -- p.text('MyList(...)') -- else: -- p.begin_group(8, 'MyList([') -- for idx, item in enumerate(self): -- if idx: -- p.text(',') -- p.breakable() -- p.pretty(item) -- p.end_group(8, '])') -- -- If you just want to indent something you can use the group function -- without open / close parameters. Under python 2.5 you can also use this -- code:: -- -- with p.indent(2): -- ... -- -- Or under python2.4 you might want to modify ``p.indentation`` by hand but -- this is rather ugly. -- -- :copyright: 2007 by Armin Ronacher. -- Portions (c) 2009 by Robert Kern. -- :license: BSD License. --""" --import __future__ --import sys --import types --import re --import datetime --from StringIO import StringIO --from collections import deque -- -- --__all__ = ['pretty', 'pprint', 'PrettyPrinter', 'RepresentationPrinter', -- 'for_type', 'for_type_by_name'] -- -- --_re_pattern_type = type(re.compile('')) -- -- --def pretty(obj, verbose=False, max_width=79, newline='\n'): -- """ -- Pretty print the object's representation. -- """ -- stream = StringIO() -- printer = RepresentationPrinter(stream, verbose, max_width, newline) -- printer.pretty(obj) -- printer.flush() -- return stream.getvalue() -- -- --def pprint(obj, verbose=False, max_width=79, newline='\n'): -- """ -- Like `pretty` but print to stdout. -- """ -- printer = RepresentationPrinter(sys.stdout, verbose, max_width, newline) -- printer.pretty(obj) -- printer.flush() -- sys.stdout.write(newline) -- sys.stdout.flush() -- -- --# add python2.5 context managers if we have the with statement feature --if hasattr(__future__, 'with_statement'): exec ''' --from __future__ import with_statement --from contextlib import contextmanager -- --class _PrettyPrinterBase(object): -- -- @contextmanager -- def indent(self, indent): -- """with statement support for indenting/dedenting.""" -- self.indentation += indent -- try: -- yield -- finally: -- self.indentation -= indent -- -- @contextmanager -- def group(self, indent=0, open='', close=''): -- """like begin_group / end_group but for the with statement.""" -- self.begin_group(indent, open) -- try: -- with self.indent(indent): -- yield -- finally: -- self.end_group(indent, close) --''' --else: -- class _PrettyPrinterBase(object): -- -- def _unsupported(self, *a, **kw): -- """unsupported operation""" -- raise RuntimeError('not available in this python version') -- group = indent = _unsupported -- del _unsupported -- -- --class PrettyPrinter(_PrettyPrinterBase): -- """ -- Baseclass for the `RepresentationPrinter` prettyprinter that is used to -- generate pretty reprs of objects. Contrary to the `RepresentationPrinter` -- this printer knows nothing about the default pprinters or the `__pretty__` -- callback method. -- """ -- -- def __init__(self, output, max_width=79, newline='\n'): -- self.output = output -- self.max_width = max_width -- self.newline = newline -- self.output_width = 0 -- self.buffer_width = 0 -- self.buffer = deque() -- -- root_group = Group(0) -- self.group_stack = [root_group] -- self.group_queue = GroupQueue(root_group) -- self.indentation = 0 -- -- def _break_outer_groups(self): -- while self.max_width < self.output_width + self.buffer_width: -- group = self.group_queue.deq() -- if not group: -- return -- while group.breakables: -- x = self.buffer.popleft() -- self.output_width = x.output(self.output, self.output_width) -- self.buffer_width -= x.width -- while self.buffer and isinstance(self.buffer[0], Text): -- x = self.buffer.popleft() -- self.output_width = x.output(self.output, self.output_width) -- self.buffer_width -= x.width -- -- def text(self, obj): -- """Add literal text to the output.""" -- width = len(obj) -- if self.buffer: -- text = self.buffer[-1] -- if not isinstance(text, Text): -- text = Text() -- self.buffer.append(text) -- text.add(obj, width) -- self.buffer_width += width -- self._break_outer_groups() -- else: -- self.output.write(obj) -- self.output_width += width -- -- def breakable(self, sep=' '): -- """ -- Add a breakable separator to the output. This does not mean that it -- will automatically break here. If no breaking on this position takes -- place the `sep` is inserted which default to one space. -- """ -- width = len(sep) -- group = self.group_stack[-1] -- if group.want_break: -- self.flush() -- self.output.write(self.newline) -- self.output.write(' ' * self.indentation) -- self.output_width = self.indentation -- self.buffer_width = 0 -- else: -- self.buffer.append(Breakable(sep, width, self)) -- self.buffer_width += width -- self._break_outer_groups() -- -- -- def begin_group(self, indent=0, open=''): -- """ -- Begin a group. If you want support for python < 2.5 which doesn't has -- the with statement this is the preferred way: -- -- p.begin_group(1, '{') -- ... -- p.end_group(1, '}') -- -- The python 2.5 expression would be this: -- -- with p.group(1, '{', '}'): -- ... -- -- The first parameter specifies the indentation for the next line (usually -- the width of the opening text), the second the opening text. All -- parameters are optional. -- """ -- if open: -- self.text(open) -- group = Group(self.group_stack[-1].depth + 1) -- self.group_stack.append(group) -- self.group_queue.enq(group) -- self.indentation += indent -- -- def end_group(self, dedent=0, close=''): -- """End a group. See `begin_group` for more details.""" -- self.indentation -= dedent -- group = self.group_stack.pop() -- if not group.breakables: -- self.group_queue.remove(group) -- if close: -- self.text(close) -- -- def flush(self): -- """Flush data that is left in the buffer.""" -- for data in self.buffer: -- self.output_width += data.output(self.output, self.output_width) -- self.buffer.clear() -- self.buffer_width = 0 -- -- --def _get_mro(obj_class): -- """ Get a reasonable method resolution order of a class and its superclasses -- for both old-style and new-style classes. -- """ -- if not hasattr(obj_class, '__mro__'): -- # Old-style class. Mix in object to make a fake new-style class. -- try: -- obj_class = type(obj_class.__name__, (obj_class, object), {}) -- except TypeError: -- # Old-style extension type that does not descend from object. -- # FIXME: try to construct a more thorough MRO. -- mro = [obj_class] -- else: -- mro = obj_class.__mro__[1:-1] -- else: -- mro = obj_class.__mro__ -- return mro -- -- --class RepresentationPrinter(PrettyPrinter): -- """ -- Special pretty printer that has a `pretty` method that calls the pretty -- printer for a python object. -- -- This class stores processing data on `self` so you must *never* use -- this class in a threaded environment. Always lock it or reinstanciate -- it. -- -- Instances also have a verbose flag callbacks can access to control their -- output. For example the default instance repr prints all attributes and -- methods that are not prefixed by an underscore if the printer is in -- verbose mode. -- """ -- -- def __init__(self, output, verbose=False, max_width=79, newline='\n'): -- PrettyPrinter.__init__(self, output, max_width, newline) -- self.verbose = verbose -- self.stack = [] -- -- def pretty(self, obj): -- """Pretty print the given object.""" -- obj_id = id(obj) -- cycle = obj_id in self.stack -- self.stack.append(obj_id) -- self.begin_group() -- try: -- obj_class = getattr(obj, '__class__', None) or type(obj) -- if hasattr(obj_class, '__pretty__'): -- return obj_class.__pretty__(obj, self, cycle) -- try: -- printer = _singleton_pprinters[obj_id] -- except (TypeError, KeyError): -- pass -- else: -- return printer(obj, self, cycle) -- for cls in _get_mro(obj_class): -- if cls in _type_pprinters: -- return _type_pprinters[cls](obj, self, cycle) -- else: -- printer = self._in_deferred_types(cls) -- if printer is not None: -- return printer(obj, self, cycle) -- return _default_pprint(obj, self, cycle) -- finally: -- self.end_group() -- self.stack.pop() -- -- def _in_deferred_types(self, cls): -- """ -- Check if the given class is specified in the deferred type registry. -- -- Returns the printer from the registry if it exists, and None if the -- class is not in the registry. Successful matches will be moved to the -- regular type registry for future use. -- """ -- mod = getattr(cls, '__module__', None) -- name = getattr(cls, '__name__', None) -- key = (mod, name) -- printer = None -- if key in _deferred_type_pprinters: -- # Move the printer over to the regular registry. -- printer = _deferred_type_pprinters.pop(key) -- _type_pprinters[cls] = printer -- return printer -- -- -- --class Printable(object): -- -- def output(self, stream, output_width): -- return output_width -- -- --class Text(Printable): -- -- def __init__(self): -- self.objs = [] -- self.width = 0 -- -- def output(self, stream, output_width): -- for obj in self.objs: -- stream.write(obj) -- return output_width + self.width -- -- def add(self, obj, width): -- self.objs.append(obj) -- self.width += width -- -- --class Breakable(Printable): -- -- def __init__(self, seq, width, pretty): -- self.obj = seq -- self.width = width -- self.pretty = pretty -- self.indentation = pretty.indentation -- self.group = pretty.group_stack[-1] -- self.group.breakables.append(self) -- -- def output(self, stream, output_width): -- self.group.breakables.popleft() -- if self.group.want_break: -- stream.write(self.pretty.newline) -- stream.write(' ' * self.indentation) -- return self.indentation -- if not self.group.breakables: -- self.pretty.group_queue.remove(self.group) -- stream.write(self.obj) -- return output_width + self.width -- -- --class Group(Printable): -- -- def __init__(self, depth): -- self.depth = depth -- self.breakables = deque() -- self.want_break = False -- -- --class GroupQueue(object): -- -- def __init__(self, *groups): -- self.queue = [] -- for group in groups: -- self.enq(group) -- -- def enq(self, group): -- depth = group.depth -- while depth > len(self.queue) - 1: -- self.queue.append([]) -- self.queue[depth].append(group) -- -- def deq(self): -- for stack in self.queue: -- for idx, group in enumerate(reversed(stack)): -- if group.breakables: -- del stack[idx] -- group.want_break = True -- return group -- for group in stack: -- group.want_break = True -- del stack[:] -- -- def remove(self, group): -- try: -- self.queue[group.depth].remove(group) -- except ValueError: -- pass -- -- --_baseclass_reprs = (object.__repr__, types.InstanceType.__repr__) -- -- --def _default_pprint(obj, p, cycle): -- """ -- The default print function. Used if an object does not provide one and -- it's none of the builtin objects. -- """ -- klass = getattr(obj, '__class__', None) or type(obj) -- if getattr(klass, '__repr__', None) not in _baseclass_reprs: -- # A user-provided repr. -- p.text(repr(obj)) -- return -- p.begin_group(1, '<') -- p.pretty(klass) -- p.text(' at 0x%x' % id(obj)) -- if cycle: -- p.text(' ...') -- elif p.verbose: -- first = True -- for key in dir(obj): -- if not key.startswith('_'): -- try: -- value = getattr(obj, key) -- except AttributeError: -- continue -- if isinstance(value, types.MethodType): -- continue -- if not first: -- p.text(',') -- p.breakable() -- p.text(key) -- p.text('=') -- step = len(key) + 1 -- p.indentation += step -- p.pretty(value) -- p.indentation -= step -- first = False -- p.end_group(1, '>') -- -- --def _seq_pprinter_factory(start, end): -- """ -- Factory that returns a pprint function useful for sequences. Used by -- the default pprint for tuples, dicts, lists, sets and frozensets. -- """ -- def inner(obj, p, cycle): -- if cycle: -- return p.text(start + '...' + end) -- step = len(start) -- p.begin_group(step, start) -- for idx, x in enumerate(obj): -- if idx: -- p.text(',') -- p.breakable() -- p.pretty(x) -- if len(obj) == 1 and type(obj) is tuple: -- # Special case for 1-item tuples. -- p.text(',') -- p.end_group(step, end) -- return inner -- -- --def _dict_pprinter_factory(start, end): -- """ -- Factory that returns a pprint function used by the default pprint of -- dicts and dict proxies. -- """ -- def inner(obj, p, cycle): -- if cycle: -- return p.text('{...}') -- p.begin_group(1, start) -- keys = obj.keys() -- try: -- keys.sort() -- except Exception, e: -- # Sometimes the keys don't sort. -- pass -- for idx, key in enumerate(keys): -- if idx: -- p.text(',') -- p.breakable() -- p.pretty(key) -- p.text(': ') -- p.pretty(obj[key]) -- p.end_group(1, end) -- return inner -- -- --def _super_pprint(obj, p, cycle): -- """The pprint for the super type.""" -- p.begin_group(8, '') -- -- --def _re_pattern_pprint(obj, p, cycle): -- """The pprint function for regular expression patterns.""" -- p.text('re.compile(') -- pattern = repr(obj.pattern) -- if pattern[:1] in 'uU': -- pattern = pattern[1:] -- prefix = 'ur' -- else: -- prefix = 'r' -- pattern = prefix + pattern.replace('\\\\', '\\') -- p.text(pattern) -- if obj.flags: -- p.text(',') -- p.breakable() -- done_one = False -- for flag in ('TEMPLATE', 'IGNORECASE', 'LOCALE', 'MULTILINE', 'DOTALL', -- 'UNICODE', 'VERBOSE', 'DEBUG'): -- if obj.flags & getattr(re, flag): -- if done_one: -- p.text('|') -- p.text('re.' + flag) -- done_one = True -- p.text(')') -- -- --def _type_pprint(obj, p, cycle): -- """The pprint for classes and types.""" -- if obj.__module__ in ('__builtin__', 'exceptions'): -- name = obj.__name__ -- else: -- name = obj.__module__ + '.' + obj.__name__ -- p.text(name) -- -- --def _repr_pprint(obj, p, cycle): -- """A pprint that just redirects to the normal repr function.""" -- p.text(repr(obj)) -- -- --def _function_pprint(obj, p, cycle): -- """Base pprint for all functions and builtin functions.""" -- if obj.__module__ in ('__builtin__', 'exceptions') or not obj.__module__: -- name = obj.__name__ -- else: -- name = obj.__module__ + '.' + obj.__name__ -- p.text('' % name) -- -- --def _exception_pprint(obj, p, cycle): -- """Base pprint for all exceptions.""" -- if obj.__class__.__module__ == 'exceptions': -- name = obj.__class__.__name__ -- else: -- name = '%s.%s' % ( -- obj.__class__.__module__, -- obj.__class__.__name__ -- ) -- step = len(name) + 1 -- p.begin_group(step, '(') -- for idx, arg in enumerate(getattr(obj, 'args', ())): -- if idx: -- p.text(',') -- p.breakable() -- p.pretty(arg) -- p.end_group(step, ')') -- -- --#: the exception base --try: -- _exception_base = BaseException --except NameError: -- _exception_base = Exception -- -- --#: printers for builtin types --_type_pprinters = { -- int: _repr_pprint, -- long: _repr_pprint, -- float: _repr_pprint, -- str: _repr_pprint, -- unicode: _repr_pprint, -- tuple: _seq_pprinter_factory('(', ')'), -- list: _seq_pprinter_factory('[', ']'), -- dict: _dict_pprinter_factory('{', '}'), -- types.DictProxyType: _dict_pprinter_factory(''), -- set: _seq_pprinter_factory('set([', '])'), -- frozenset: _seq_pprinter_factory('frozenset([', '])'), -- super: _super_pprint, -- _re_pattern_type: _re_pattern_pprint, -- type: _type_pprint, -- types.ClassType: _type_pprint, -- types.FunctionType: _function_pprint, -- types.BuiltinFunctionType: _function_pprint, -- types.SliceType: _repr_pprint, -- types.MethodType: _repr_pprint, -- xrange: _repr_pprint, -- datetime.datetime: _repr_pprint, -- datetime.timedelta: _repr_pprint, -- _exception_base: _exception_pprint --} -- --#: printers for types specified by name --_deferred_type_pprinters = { --} -- --def for_type(typ, func): -- """ -- Add a pretty printer for a given type. -- """ -- oldfunc = _type_pprinters.get(typ, None) -- if func is not None: -- # To support easy restoration of old pprinters, we need to ignore Nones. -- _type_pprinters[typ] = func -- return oldfunc -- --def for_type_by_name(type_module, type_name, func): -- """ -- Add a pretty printer for a type specified by the module and name of a type -- rather than the type object itself. -- """ -- key = (type_module, type_name) -- oldfunc = _deferred_type_pprinters.get(key, None) -- if func is not None: -- # To support easy restoration of old pprinters, we need to ignore Nones. -- _deferred_type_pprinters[key] = func -- return oldfunc -- -- --#: printers for the default singletons --_singleton_pprinters = dict.fromkeys(map(id, [None, True, False, Ellipsis, -- NotImplemented]), _repr_pprint) -- -- --if __name__ == '__main__': -- from random import randrange -- class Foo(object): -- def __init__(self): -- self.foo = 1 -- self.bar = re.compile(r'\s+') -- self.blub = dict.fromkeys(range(30), randrange(1, 40)) -- self.hehe = 23424.234234 -- self.list = ["blub", "blah", self] -- -- def get_foo(self): -- print "foo" -- -- pprint(Foo(), verbose=True) Index: ipython-0.10/IPython/external/simplegeneric/__init__.py =================================================================== --- /dev/null @@ -14287,294 +77,6 @@ Index: ipython-0.10/IPython/external/simplegeneric/__init__.py + from simplegeneric import * +except ImportError: + from _simplegeneric import * -Index: ipython-0.10/IPython/external/simplegeneric.py -=================================================================== ---- ipython-0.10.orig/IPython/external/simplegeneric.py -+++ /dev/null -@@ -1,139 +0,0 @@ --#Name: simplegeneric --#Version: 0.6 --#Summary: Simple generic functions (similar to Python's own len(), pickle.dump(), etc.) --#Home-page: http://cheeseshop.python.org/pypi/simplegeneric --#Author: Phillip J. Eby --#Author-email: peak@eby-sarna.com --#License: PSF or ZPL -- --# This is version 0.6 of Philip J. Eby's simplegeneric module --# (http://cheeseshop.python.org/pypi/simplegeneric) patched to work --# with Python 2.3 (which doesn't support assigning to __name__) -- --__all__ = ["generic"] -- -- --from types import ClassType, InstanceType --classtypes = type, ClassType -- --def generic(func): -- """Create a simple generic function""" -- -- _sentinel = object() -- -- def _by_class(*args, **kw): -- cls = args[0].__class__ -- for t in type(cls.__name__, (cls,object), {}).__mro__: -- f = _gbt(t, _sentinel) -- if f is not _sentinel: -- return f(*args, **kw) -- else: -- return func(*args, **kw) -- -- _by_type = {object: func, InstanceType: _by_class} -- _gbt = _by_type.get -- -- def when_type(t): -- """Decorator to add a method that will be called for type `t`""" -- if not isinstance(t, classtypes): -- raise TypeError( -- "%r is not a type or class" % (t,) -- ) -- def decorate(f): -- if _by_type.setdefault(t,f) is not f: -- raise TypeError( -- "%r already has method for type %r" % (func, t) -- ) -- return f -- return decorate -- -- -- -- -- -- -- _by_object = {} -- _gbo = _by_object.get -- -- def when_object(o): -- """Decorator to add a method that will be called for object `o`""" -- def decorate(f): -- if _by_object.setdefault(id(o), (o,f))[1] is not f: -- raise TypeError( -- "%r already has method for object %r" % (func, o) -- ) -- return f -- return decorate -- -- -- def dispatch(*args, **kw): -- f = _gbo(id(args[0]), _sentinel) -- if f is _sentinel: -- for t in type(args[0]).__mro__: -- f = _gbt(t, _sentinel) -- if f is not _sentinel: -- return f(*args, **kw) -- else: -- return func(*args, **kw) -- else: -- return f[1](*args, **kw) -- -- try: -- dispatch.__name__ = func.__name__ -- except TypeError: -- pass -- dispatch.__dict__ = func.__dict__.copy() -- dispatch.__doc__ = func.__doc__ -- dispatch.__module__ = func.__module__ -- -- dispatch.when_type = when_type -- dispatch.when_object = when_object -- dispatch.default = func -- dispatch.has_object = lambda o: id(o) in _by_object -- dispatch.has_type = lambda t: t in _by_type -- return dispatch -- -- -- -- --def test_suite(): -- import doctest -- return doctest.DocFileSuite( -- 'README.txt', -- optionflags=doctest.ELLIPSIS|doctest.REPORT_ONLY_FIRST_FAILURE, -- ) -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -Index: ipython-0.10/IPython/external/simplegeneric/_simplegeneric.py -=================================================================== ---- /dev/null -+++ ipython-0.10/IPython/external/simplegeneric/_simplegeneric.py -@@ -0,0 +1,139 @@ -+#Name: simplegeneric -+#Version: 0.6 -+#Summary: Simple generic functions (similar to Python's own len(), pickle.dump(), etc.) -+#Home-page: http://cheeseshop.python.org/pypi/simplegeneric -+#Author: Phillip J. Eby -+#Author-email: peak@eby-sarna.com -+#License: PSF or ZPL -+ -+# This is version 0.6 of Philip J. Eby's simplegeneric module -+# (http://cheeseshop.python.org/pypi/simplegeneric) patched to work -+# with Python 2.3 (which doesn't support assigning to __name__) -+ -+__all__ = ["generic"] -+ -+ -+from types import ClassType, InstanceType -+classtypes = type, ClassType -+ -+def generic(func): -+ """Create a simple generic function""" -+ -+ _sentinel = object() -+ -+ def _by_class(*args, **kw): -+ cls = args[0].__class__ -+ for t in type(cls.__name__, (cls,object), {}).__mro__: -+ f = _gbt(t, _sentinel) -+ if f is not _sentinel: -+ return f(*args, **kw) -+ else: -+ return func(*args, **kw) -+ -+ _by_type = {object: func, InstanceType: _by_class} -+ _gbt = _by_type.get -+ -+ def when_type(t): -+ """Decorator to add a method that will be called for type `t`""" -+ if not isinstance(t, classtypes): -+ raise TypeError( -+ "%r is not a type or class" % (t,) -+ ) -+ def decorate(f): -+ if _by_type.setdefault(t,f) is not f: -+ raise TypeError( -+ "%r already has method for type %r" % (func, t) -+ ) -+ return f -+ return decorate -+ -+ -+ -+ -+ -+ -+ _by_object = {} -+ _gbo = _by_object.get -+ -+ def when_object(o): -+ """Decorator to add a method that will be called for object `o`""" -+ def decorate(f): -+ if _by_object.setdefault(id(o), (o,f))[1] is not f: -+ raise TypeError( -+ "%r already has method for object %r" % (func, o) -+ ) -+ return f -+ return decorate -+ -+ -+ def dispatch(*args, **kw): -+ f = _gbo(id(args[0]), _sentinel) -+ if f is _sentinel: -+ for t in type(args[0]).__mro__: -+ f = _gbt(t, _sentinel) -+ if f is not _sentinel: -+ return f(*args, **kw) -+ else: -+ return func(*args, **kw) -+ else: -+ return f[1](*args, **kw) -+ -+ try: -+ dispatch.__name__ = func.__name__ -+ except TypeError: -+ pass -+ dispatch.__dict__ = func.__dict__.copy() -+ dispatch.__doc__ = func.__doc__ -+ dispatch.__module__ = func.__module__ -+ -+ dispatch.when_type = when_type -+ dispatch.when_object = when_object -+ dispatch.default = func -+ dispatch.has_object = lambda o: id(o) in _by_object -+ dispatch.has_type = lambda t: t in _by_type -+ return dispatch -+ -+ -+ -+ -+def test_suite(): -+ import doctest -+ return doctest.DocFileSuite( -+ 'README.txt', -+ optionflags=doctest.ELLIPSIS|doctest.REPORT_ONLY_FIRST_FAILURE, -+ ) -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ Index: ipython-0.10/IPython/external/validate/__init__.py =================================================================== --- /dev/null @@ -14588,2844 +90,6 @@ Index: ipython-0.10/IPython/external/validate/__init__.py + from validate import * +except ImportError: + from _validate import * -Index: ipython-0.10/IPython/external/validate.py -=================================================================== ---- ipython-0.10.orig/IPython/external/validate.py -+++ /dev/null -@@ -1,1414 +0,0 @@ --# validate.py --# A Validator object --# Copyright (C) 2005 Michael Foord, Mark Andrews, Nicola Larosa --# E-mail: fuzzyman AT voidspace DOT org DOT uk --# mark AT la-la DOT com --# nico AT tekNico DOT net -- --# This software is licensed under the terms of the BSD license. --# http://www.voidspace.org.uk/python/license.shtml --# Basically you're free to copy, modify, distribute and relicense it, --# So long as you keep a copy of the license with it. -- --# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml --# For information about bugfixes, updates and support, please join the --# ConfigObj mailing list: --# http://lists.sourceforge.net/lists/listinfo/configobj-develop --# Comments, suggestions and bug reports welcome. -- --""" -- The Validator object is used to check that supplied values -- conform to a specification. -- -- The value can be supplied as a string - e.g. from a config file. -- In this case the check will also *convert* the value to -- the required type. This allows you to add validation -- as a transparent layer to access data stored as strings. -- The validation checks that the data is correct *and* -- converts it to the expected type. -- -- Some standard checks are provided for basic data types. -- Additional checks are easy to write. They can be -- provided when the ``Validator`` is instantiated or -- added afterwards. -- -- The standard functions work with the following basic data types : -- -- * integers -- * floats -- * booleans -- * strings -- * ip_addr -- -- plus lists of these datatypes -- -- Adding additional checks is done through coding simple functions. -- -- The full set of standard checks are : -- -- * 'integer': matches integer values (including negative) -- Takes optional 'min' and 'max' arguments : :: -- -- integer() -- integer(3, 9) # any value from 3 to 9 -- integer(min=0) # any positive value -- integer(max=9) -- -- * 'float': matches float values -- Has the same parameters as the integer check. -- -- * 'boolean': matches boolean values - ``True`` or ``False`` -- Acceptable string values for True are : -- true, on, yes, 1 -- Acceptable string values for False are : -- false, off, no, 0 -- -- Any other value raises an error. -- -- * 'ip_addr': matches an Internet Protocol address, v.4, represented -- by a dotted-quad string, i.e. '1.2.3.4'. -- -- * 'string': matches any string. -- Takes optional keyword args 'min' and 'max' -- to specify min and max lengths of the string. -- -- * 'list': matches any list. -- Takes optional keyword args 'min', and 'max' to specify min and -- max sizes of the list. (Always returns a list.) -- -- * 'tuple': matches any tuple. -- Takes optional keyword args 'min', and 'max' to specify min and -- max sizes of the tuple. (Always returns a tuple.) -- -- * 'int_list': Matches a list of integers. -- Takes the same arguments as list. -- -- * 'float_list': Matches a list of floats. -- Takes the same arguments as list. -- -- * 'bool_list': Matches a list of boolean values. -- Takes the same arguments as list. -- -- * 'ip_addr_list': Matches a list of IP addresses. -- Takes the same arguments as list. -- -- * 'string_list': Matches a list of strings. -- Takes the same arguments as list. -- -- * 'mixed_list': Matches a list with different types in -- specific positions. List size must match -- the number of arguments. -- -- Each position can be one of : -- 'integer', 'float', 'ip_addr', 'string', 'boolean' -- -- So to specify a list with two strings followed -- by two integers, you write the check as : :: -- -- mixed_list('string', 'string', 'integer', 'integer') -- -- * 'pass': This check matches everything ! It never fails -- and the value is unchanged. -- -- It is also the default if no check is specified. -- -- * 'option': This check matches any from a list of options. -- You specify this check with : :: -- -- option('option 1', 'option 2', 'option 3') -- -- You can supply a default value (returned if no value is supplied) -- using the default keyword argument. -- -- You specify a list argument for default using a list constructor syntax in -- the check : :: -- -- checkname(arg1, arg2, default=list('val 1', 'val 2', 'val 3')) -- -- A badly formatted set of arguments will raise a ``VdtParamError``. --""" -- --__docformat__ = "restructuredtext en" -- --__version__ = '0.3.2' -- --__revision__ = '$Id: validate.py 123 2005-09-08 08:54:28Z fuzzyman $' -- --__all__ = ( -- '__version__', -- 'dottedQuadToNum', -- 'numToDottedQuad', -- 'ValidateError', -- 'VdtUnknownCheckError', -- 'VdtParamError', -- 'VdtTypeError', -- 'VdtValueError', -- 'VdtValueTooSmallError', -- 'VdtValueTooBigError', -- 'VdtValueTooShortError', -- 'VdtValueTooLongError', -- 'VdtMissingValue', -- 'Validator', -- 'is_integer', -- 'is_float', -- 'is_boolean', -- 'is_list', -- 'is_tuple', -- 'is_ip_addr', -- 'is_string', -- 'is_int_list', -- 'is_bool_list', -- 'is_float_list', -- 'is_string_list', -- 'is_ip_addr_list', -- 'is_mixed_list', -- 'is_option', -- '__docformat__', --) -- -- --import sys --INTP_VER = sys.version_info[:2] --if INTP_VER < (2, 2): -- raise RuntimeError("Python v.2.2 or later needed") -- --import re --StringTypes = (str, unicode) -- -- --_list_arg = re.compile(r''' -- (?: -- ([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*list\( -- ( -- (?: -- \s* -- (?: -- (?:".*?")| # double quotes -- (?:'.*?')| # single quotes -- (?:[^'",\s\)][^,\)]*?) # unquoted -- ) -- \s*,\s* -- )* -- (?: -- (?:".*?")| # double quotes -- (?:'.*?')| # single quotes -- (?:[^'",\s\)][^,\)]*?) # unquoted -- )? # last one -- ) -- \) -- ) --''', re.VERBOSE) # two groups -- --_list_members = re.compile(r''' -- ( -- (?:".*?")| # double quotes -- (?:'.*?')| # single quotes -- (?:[^'",\s=][^,=]*?) # unquoted -- ) -- (?: -- (?:\s*,\s*)|(?:\s*$) # comma -- ) --''', re.VERBOSE) # one group -- --_paramstring = r''' -- (?: -- ( -- (?: -- [a-zA-Z_][a-zA-Z0-9_]*\s*=\s*list\( -- (?: -- \s* -- (?: -- (?:".*?")| # double quotes -- (?:'.*?')| # single quotes -- (?:[^'",\s\)][^,\)]*?) # unquoted -- ) -- \s*,\s* -- )* -- (?: -- (?:".*?")| # double quotes -- (?:'.*?')| # single quotes -- (?:[^'",\s\)][^,\)]*?) # unquoted -- )? # last one -- \) -- )| -- (?: -- (?:".*?")| # double quotes -- (?:'.*?')| # single quotes -- (?:[^'",\s=][^,=]*?)| # unquoted -- (?: # keyword argument -- [a-zA-Z_][a-zA-Z0-9_]*\s*=\s* -- (?: -- (?:".*?")| # double quotes -- (?:'.*?')| # single quotes -- (?:[^'",\s=][^,=]*?) # unquoted -- ) -- ) -- ) -- ) -- (?: -- (?:\s*,\s*)|(?:\s*$) # comma -- ) -- ) -- ''' -- --_matchstring = '^%s*' % _paramstring -- --# Python pre 2.2.1 doesn't have bool --try: -- bool --except NameError: -- def bool(val): -- """Simple boolean equivalent function. """ -- if val: -- return 1 -- else: -- return 0 -- -- --def dottedQuadToNum(ip): -- """ -- Convert decimal dotted quad string to long integer -- -- >>> dottedQuadToNum('1 ') -- 1L -- >>> dottedQuadToNum(' 1.2') -- 16777218L -- >>> dottedQuadToNum(' 1.2.3 ') -- 16908291L -- >>> dottedQuadToNum('1.2.3.4') -- 16909060L -- >>> dottedQuadToNum('1.2.3. 4') -- Traceback (most recent call last): -- ValueError: Not a good dotted-quad IP: 1.2.3. 4 -- >>> dottedQuadToNum('255.255.255.255') -- 4294967295L -- >>> dottedQuadToNum('255.255.255.256') -- Traceback (most recent call last): -- ValueError: Not a good dotted-quad IP: 255.255.255.256 -- """ -- -- # import here to avoid it when ip_addr values are not used -- import socket, struct -- -- try: -- return struct.unpack('!L', -- socket.inet_aton(ip.strip()))[0] -- except socket.error: -- # bug in inet_aton, corrected in Python 2.3 -- if ip.strip() == '255.255.255.255': -- return 0xFFFFFFFFL -- else: -- raise ValueError('Not a good dotted-quad IP: %s' % ip) -- return -- -- --def numToDottedQuad(num): -- """ -- Convert long int to dotted quad string -- -- >>> numToDottedQuad(-1L) -- Traceback (most recent call last): -- ValueError: Not a good numeric IP: -1 -- >>> numToDottedQuad(1L) -- '0.0.0.1' -- >>> numToDottedQuad(16777218L) -- '1.0.0.2' -- >>> numToDottedQuad(16908291L) -- '1.2.0.3' -- >>> numToDottedQuad(16909060L) -- '1.2.3.4' -- >>> numToDottedQuad(4294967295L) -- '255.255.255.255' -- >>> numToDottedQuad(4294967296L) -- Traceback (most recent call last): -- ValueError: Not a good numeric IP: 4294967296 -- """ -- -- # import here to avoid it when ip_addr values are not used -- import socket, struct -- -- # no need to intercept here, 4294967295L is fine -- try: -- return socket.inet_ntoa( -- struct.pack('!L', long(num))) -- except (socket.error, struct.error, OverflowError): -- raise ValueError('Not a good numeric IP: %s' % num) -- -- --class ValidateError(Exception): -- """ -- This error indicates that the check failed. -- It can be the base class for more specific errors. -- -- Any check function that fails ought to raise this error. -- (or a subclass) -- -- >>> raise ValidateError -- Traceback (most recent call last): -- ValidateError -- """ -- -- --class VdtMissingValue(ValidateError): -- """No value was supplied to a check that needed one.""" -- -- --class VdtUnknownCheckError(ValidateError): -- """An unknown check function was requested""" -- -- def __init__(self, value): -- """ -- >>> raise VdtUnknownCheckError('yoda') -- Traceback (most recent call last): -- VdtUnknownCheckError: the check "yoda" is unknown. -- """ -- ValidateError.__init__(self, 'the check "%s" is unknown.' % (value,)) -- -- --class VdtParamError(SyntaxError): -- """An incorrect parameter was passed""" -- -- def __init__(self, name, value): -- """ -- >>> raise VdtParamError('yoda', 'jedi') -- Traceback (most recent call last): -- VdtParamError: passed an incorrect value "jedi" for parameter "yoda". -- """ -- SyntaxError.__init__(self, 'passed an incorrect value "%s" for parameter "%s".' % (value, name)) -- -- --class VdtTypeError(ValidateError): -- """The value supplied was of the wrong type""" -- -- def __init__(self, value): -- """ -- >>> raise VdtTypeError('jedi') -- Traceback (most recent call last): -- VdtTypeError: the value "jedi" is of the wrong type. -- """ -- ValidateError.__init__(self, 'the value "%s" is of the wrong type.' % (value,)) -- -- --class VdtValueError(ValidateError): -- """The value supplied was of the correct type, but was not an allowed value.""" -- -- def __init__(self, value): -- """ -- >>> raise VdtValueError('jedi') -- Traceback (most recent call last): -- VdtValueError: the value "jedi" is unacceptable. -- """ -- ValidateError.__init__(self, 'the value "%s" is unacceptable.' % (value,)) -- -- --class VdtValueTooSmallError(VdtValueError): -- """The value supplied was of the correct type, but was too small.""" -- -- def __init__(self, value): -- """ -- >>> raise VdtValueTooSmallError('0') -- Traceback (most recent call last): -- VdtValueTooSmallError: the value "0" is too small. -- """ -- ValidateError.__init__(self, 'the value "%s" is too small.' % (value,)) -- -- --class VdtValueTooBigError(VdtValueError): -- """The value supplied was of the correct type, but was too big.""" -- -- def __init__(self, value): -- """ -- >>> raise VdtValueTooBigError('1') -- Traceback (most recent call last): -- VdtValueTooBigError: the value "1" is too big. -- """ -- ValidateError.__init__(self, 'the value "%s" is too big.' % (value,)) -- -- --class VdtValueTooShortError(VdtValueError): -- """The value supplied was of the correct type, but was too short.""" -- -- def __init__(self, value): -- """ -- >>> raise VdtValueTooShortError('jed') -- Traceback (most recent call last): -- VdtValueTooShortError: the value "jed" is too short. -- """ -- ValidateError.__init__( -- self, -- 'the value "%s" is too short.' % (value,)) -- -- --class VdtValueTooLongError(VdtValueError): -- """The value supplied was of the correct type, but was too long.""" -- -- def __init__(self, value): -- """ -- >>> raise VdtValueTooLongError('jedie') -- Traceback (most recent call last): -- VdtValueTooLongError: the value "jedie" is too long. -- """ -- ValidateError.__init__(self, 'the value "%s" is too long.' % (value,)) -- -- --class Validator(object): -- """ -- Validator is an object that allows you to register a set of 'checks'. -- These checks take input and test that it conforms to the check. -- -- This can also involve converting the value from a string into -- the correct datatype. -- -- The ``check`` method takes an input string which configures which -- check is to be used and applies that check to a supplied value. -- -- An example input string would be: -- 'int_range(param1, param2)' -- -- You would then provide something like: -- -- >>> def int_range_check(value, min, max): -- ... # turn min and max from strings to integers -- ... min = int(min) -- ... max = int(max) -- ... # check that value is of the correct type. -- ... # possible valid inputs are integers or strings -- ... # that represent integers -- ... if not isinstance(value, (int, long, StringTypes)): -- ... raise VdtTypeError(value) -- ... elif isinstance(value, StringTypes): -- ... # if we are given a string -- ... # attempt to convert to an integer -- ... try: -- ... value = int(value) -- ... except ValueError: -- ... raise VdtValueError(value) -- ... # check the value is between our constraints -- ... if not min <= value: -- ... raise VdtValueTooSmallError(value) -- ... if not value <= max: -- ... raise VdtValueTooBigError(value) -- ... return value -- -- >>> fdict = {'int_range': int_range_check} -- >>> vtr1 = Validator(fdict) -- >>> vtr1.check('int_range(20, 40)', '30') -- 30 -- >>> vtr1.check('int_range(20, 40)', '60') -- Traceback (most recent call last): -- VdtValueTooBigError: the value "60" is too big. -- -- New functions can be added with : :: -- -- >>> vtr2 = Validator() -- >>> vtr2.functions['int_range'] = int_range_check -- -- Or by passing in a dictionary of functions when Validator -- is instantiated. -- -- Your functions *can* use keyword arguments, -- but the first argument should always be 'value'. -- -- If the function doesn't take additional arguments, -- the parentheses are optional in the check. -- It can be written with either of : :: -- -- keyword = function_name -- keyword = function_name() -- -- The first program to utilise Validator() was Michael Foord's -- ConfigObj, an alternative to ConfigParser which supports lists and -- can validate a config file using a config schema. -- For more details on using Validator with ConfigObj see: -- http://www.voidspace.org.uk/python/configobj.html -- """ -- -- # this regex does the initial parsing of the checks -- _func_re = re.compile(r'(.+?)\((.*)\)') -- -- # this regex takes apart keyword arguments -- _key_arg = re.compile(r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.*)$') -- -- -- # this regex finds keyword=list(....) type values -- _list_arg = _list_arg -- -- # this regex takes individual values out of lists - in one pass -- _list_members = _list_members -- -- # These regexes check a set of arguments for validity -- # and then pull the members out -- _paramfinder = re.compile(_paramstring, re.VERBOSE) -- _matchfinder = re.compile(_matchstring, re.VERBOSE) -- -- -- def __init__(self, functions=None): -- """ -- >>> vtri = Validator() -- """ -- self.functions = { -- '': self._pass, -- 'integer': is_integer, -- 'float': is_float, -- 'boolean': is_boolean, -- 'ip_addr': is_ip_addr, -- 'string': is_string, -- 'list': is_list, -- 'tuple': is_tuple, -- 'int_list': is_int_list, -- 'float_list': is_float_list, -- 'bool_list': is_bool_list, -- 'ip_addr_list': is_ip_addr_list, -- 'string_list': is_string_list, -- 'mixed_list': is_mixed_list, -- 'pass': self._pass, -- 'option': is_option, -- } -- if functions is not None: -- self.functions.update(functions) -- # tekNico: for use by ConfigObj -- self.baseErrorClass = ValidateError -- self._cache = {} -- -- -- def check(self, check, value, missing=False): -- """ -- Usage: check(check, value) -- -- Arguments: -- check: string representing check to apply (including arguments) -- value: object to be checked -- Returns value, converted to correct type if necessary -- -- If the check fails, raises a ``ValidateError`` subclass. -- -- >>> vtor.check('yoda', '') -- Traceback (most recent call last): -- VdtUnknownCheckError: the check "yoda" is unknown. -- >>> vtor.check('yoda()', '') -- Traceback (most recent call last): -- VdtUnknownCheckError: the check "yoda" is unknown. -- -- >>> vtor.check('string(default="")', '', missing=True) -- '' -- """ -- fun_name, fun_args, fun_kwargs, default = self._parse_with_caching(check) -- -- if missing: -- if default is None: -- # no information needed here - to be handled by caller -- raise VdtMissingValue() -- value = self._handle_none(default) -- -- if value is None: -- return None -- -- return self._check_value(value, fun_name, fun_args, fun_kwargs) -- -- -- def _handle_none(self, value): -- if value == 'None': -- value = None -- elif value in ("'None'", '"None"'): -- # Special case a quoted None -- value = self._unquote(value) -- return value -- -- -- def _parse_with_caching(self, check): -- if check in self._cache: -- fun_name, fun_args, fun_kwargs, default = self._cache[check] -- # We call list and dict below to work with *copies* of the data -- # rather than the original (which are mutable of course) -- fun_args = list(fun_args) -- fun_kwargs = dict(fun_kwargs) -- else: -- fun_name, fun_args, fun_kwargs, default = self._parse_check(check) -- fun_kwargs = dict((str(key), value) for (key, value) in fun_kwargs.items()) -- self._cache[check] = fun_name, list(fun_args), dict(fun_kwargs), default -- return fun_name, fun_args, fun_kwargs, default -- -- -- def _check_value(self, value, fun_name, fun_args, fun_kwargs): -- try: -- fun = self.functions[fun_name] -- except KeyError: -- raise VdtUnknownCheckError(fun_name) -- else: -- return fun(value, *fun_args, **fun_kwargs) -- -- -- def _parse_check(self, check): -- fun_match = self._func_re.match(check) -- if fun_match: -- fun_name = fun_match.group(1) -- arg_string = fun_match.group(2) -- arg_match = self._matchfinder.match(arg_string) -- if arg_match is None: -- # Bad syntax -- raise VdtParamError('Bad syntax in check "%s".' % check) -- fun_args = [] -- fun_kwargs = {} -- # pull out args of group 2 -- for arg in self._paramfinder.findall(arg_string): -- # args may need whitespace removing (before removing quotes) -- arg = arg.strip() -- listmatch = self._list_arg.match(arg) -- if listmatch: -- key, val = self._list_handle(listmatch) -- fun_kwargs[key] = val -- continue -- keymatch = self._key_arg.match(arg) -- if keymatch: -- val = keymatch.group(2) -- if not val in ("'None'", '"None"'): -- # Special case a quoted None -- val = self._unquote(val) -- fun_kwargs[keymatch.group(1)] = val -- continue -- -- fun_args.append(self._unquote(arg)) -- else: -- # allows for function names without (args) -- return check, (), {}, None -- -- # Default must be deleted if the value is specified too, -- # otherwise the check function will get a spurious "default" keyword arg -- try: -- default = fun_kwargs.pop('default', None) -- except AttributeError: -- # Python 2.2 compatibility -- default = None -- try: -- default = fun_kwargs['default'] -- del fun_kwargs['default'] -- except KeyError: -- pass -- -- return fun_name, fun_args, fun_kwargs, default -- -- -- def _unquote(self, val): -- """Unquote a value if necessary.""" -- if (len(val) >= 2) and (val[0] in ("'", '"')) and (val[0] == val[-1]): -- val = val[1:-1] -- return val -- -- -- def _list_handle(self, listmatch): -- """Take apart a ``keyword=list('val, 'val')`` type string.""" -- out = [] -- name = listmatch.group(1) -- args = listmatch.group(2) -- for arg in self._list_members.findall(args): -- out.append(self._unquote(arg)) -- return name, out -- -- -- def _pass(self, value): -- """ -- Dummy check that always passes -- -- >>> vtor.check('', 0) -- 0 -- >>> vtor.check('', '0') -- '0' -- """ -- return value -- -- -- def get_default_value(self, check): -- """ -- Given a check, return the default value for the check -- (converted to the right type). -- -- If the check doesn't specify a default value then a -- ``KeyError`` will be raised. -- """ -- fun_name, fun_args, fun_kwargs, default = self._parse_with_caching(check) -- if default is None: -- raise KeyError('Check "%s" has no default value.' % check) -- value = self._handle_none(default) -- if value is None: -- return value -- return self._check_value(value, fun_name, fun_args, fun_kwargs) -- -- --def _is_num_param(names, values, to_float=False): -- """ -- Return numbers from inputs or raise VdtParamError. -- -- Lets ``None`` pass through. -- Pass in keyword argument ``to_float=True`` to -- use float for the conversion rather than int. -- -- >>> _is_num_param(('', ''), (0, 1.0)) -- [0, 1] -- >>> _is_num_param(('', ''), (0, 1.0), to_float=True) -- [0.0, 1.0] -- >>> _is_num_param(('a'), ('a')) -- Traceback (most recent call last): -- VdtParamError: passed an incorrect value "a" for parameter "a". -- """ -- fun = to_float and float or int -- out_params = [] -- for (name, val) in zip(names, values): -- if val is None: -- out_params.append(val) -- elif isinstance(val, (int, long, float, StringTypes)): -- try: -- out_params.append(fun(val)) -- except ValueError, e: -- raise VdtParamError(name, val) -- else: -- raise VdtParamError(name, val) -- return out_params -- -- --# built in checks --# you can override these by setting the appropriate name --# in Validator.functions --# note: if the params are specified wrongly in your input string, --# you will also raise errors. -- --def is_integer(value, min=None, max=None): -- """ -- A check that tests that a given value is an integer (int, or long) -- and optionally, between bounds. A negative value is accepted, while -- a float will fail. -- -- If the value is a string, then the conversion is done - if possible. -- Otherwise a VdtError is raised. -- -- >>> vtor.check('integer', '-1') -- -1 -- >>> vtor.check('integer', '0') -- 0 -- >>> vtor.check('integer', 9) -- 9 -- >>> vtor.check('integer', 'a') -- Traceback (most recent call last): -- VdtTypeError: the value "a" is of the wrong type. -- >>> vtor.check('integer', '2.2') -- Traceback (most recent call last): -- VdtTypeError: the value "2.2" is of the wrong type. -- >>> vtor.check('integer(10)', '20') -- 20 -- >>> vtor.check('integer(max=20)', '15') -- 15 -- >>> vtor.check('integer(10)', '9') -- Traceback (most recent call last): -- VdtValueTooSmallError: the value "9" is too small. -- >>> vtor.check('integer(10)', 9) -- Traceback (most recent call last): -- VdtValueTooSmallError: the value "9" is too small. -- >>> vtor.check('integer(max=20)', '35') -- Traceback (most recent call last): -- VdtValueTooBigError: the value "35" is too big. -- >>> vtor.check('integer(max=20)', 35) -- Traceback (most recent call last): -- VdtValueTooBigError: the value "35" is too big. -- >>> vtor.check('integer(0, 9)', False) -- 0 -- """ -- (min_val, max_val) = _is_num_param(('min', 'max'), (min, max)) -- if not isinstance(value, (int, long, StringTypes)): -- raise VdtTypeError(value) -- if isinstance(value, StringTypes): -- # if it's a string - does it represent an integer ? -- try: -- value = int(value) -- except ValueError: -- raise VdtTypeError(value) -- if (min_val is not None) and (value < min_val): -- raise VdtValueTooSmallError(value) -- if (max_val is not None) and (value > max_val): -- raise VdtValueTooBigError(value) -- return value -- -- --def is_float(value, min=None, max=None): -- """ -- A check that tests that a given value is a float -- (an integer will be accepted), and optionally - that it is between bounds. -- -- If the value is a string, then the conversion is done - if possible. -- Otherwise a VdtError is raised. -- -- This can accept negative values. -- -- >>> vtor.check('float', '2') -- 2.0 -- -- From now on we multiply the value to avoid comparing decimals -- -- >>> vtor.check('float', '-6.8') * 10 -- -68.0 -- >>> vtor.check('float', '12.2') * 10 -- 122.0 -- >>> vtor.check('float', 8.4) * 10 -- 84.0 -- >>> vtor.check('float', 'a') -- Traceback (most recent call last): -- VdtTypeError: the value "a" is of the wrong type. -- >>> vtor.check('float(10.1)', '10.2') * 10 -- 102.0 -- >>> vtor.check('float(max=20.2)', '15.1') * 10 -- 151.0 -- >>> vtor.check('float(10.0)', '9.0') -- Traceback (most recent call last): -- VdtValueTooSmallError: the value "9.0" is too small. -- >>> vtor.check('float(max=20.0)', '35.0') -- Traceback (most recent call last): -- VdtValueTooBigError: the value "35.0" is too big. -- """ -- (min_val, max_val) = _is_num_param( -- ('min', 'max'), (min, max), to_float=True) -- if not isinstance(value, (int, long, float, StringTypes)): -- raise VdtTypeError(value) -- if not isinstance(value, float): -- # if it's a string - does it represent a float ? -- try: -- value = float(value) -- except ValueError: -- raise VdtTypeError(value) -- if (min_val is not None) and (value < min_val): -- raise VdtValueTooSmallError(value) -- if (max_val is not None) and (value > max_val): -- raise VdtValueTooBigError(value) -- return value -- -- --bool_dict = { -- True: True, 'on': True, '1': True, 'true': True, 'yes': True, -- False: False, 'off': False, '0': False, 'false': False, 'no': False, --} -- -- --def is_boolean(value): -- """ -- Check if the value represents a boolean. -- -- >>> vtor.check('boolean', 0) -- 0 -- >>> vtor.check('boolean', False) -- 0 -- >>> vtor.check('boolean', '0') -- 0 -- >>> vtor.check('boolean', 'off') -- 0 -- >>> vtor.check('boolean', 'false') -- 0 -- >>> vtor.check('boolean', 'no') -- 0 -- >>> vtor.check('boolean', 'nO') -- 0 -- >>> vtor.check('boolean', 'NO') -- 0 -- >>> vtor.check('boolean', 1) -- 1 -- >>> vtor.check('boolean', True) -- 1 -- >>> vtor.check('boolean', '1') -- 1 -- >>> vtor.check('boolean', 'on') -- 1 -- >>> vtor.check('boolean', 'true') -- 1 -- >>> vtor.check('boolean', 'yes') -- 1 -- >>> vtor.check('boolean', 'Yes') -- 1 -- >>> vtor.check('boolean', 'YES') -- 1 -- >>> vtor.check('boolean', '') -- Traceback (most recent call last): -- VdtTypeError: the value "" is of the wrong type. -- >>> vtor.check('boolean', 'up') -- Traceback (most recent call last): -- VdtTypeError: the value "up" is of the wrong type. -- -- """ -- if isinstance(value, StringTypes): -- try: -- return bool_dict[value.lower()] -- except KeyError: -- raise VdtTypeError(value) -- # we do an equality test rather than an identity test -- # this ensures Python 2.2 compatibilty -- # and allows 0 and 1 to represent True and False -- if value == False: -- return False -- elif value == True: -- return True -- else: -- raise VdtTypeError(value) -- -- --def is_ip_addr(value): -- """ -- Check that the supplied value is an Internet Protocol address, v.4, -- represented by a dotted-quad string, i.e. '1.2.3.4'. -- -- >>> vtor.check('ip_addr', '1 ') -- '1' -- >>> vtor.check('ip_addr', ' 1.2') -- '1.2' -- >>> vtor.check('ip_addr', ' 1.2.3 ') -- '1.2.3' -- >>> vtor.check('ip_addr', '1.2.3.4') -- '1.2.3.4' -- >>> vtor.check('ip_addr', '0.0.0.0') -- '0.0.0.0' -- >>> vtor.check('ip_addr', '255.255.255.255') -- '255.255.255.255' -- >>> vtor.check('ip_addr', '255.255.255.256') -- Traceback (most recent call last): -- VdtValueError: the value "255.255.255.256" is unacceptable. -- >>> vtor.check('ip_addr', '1.2.3.4.5') -- Traceback (most recent call last): -- VdtValueError: the value "1.2.3.4.5" is unacceptable. -- >>> vtor.check('ip_addr', '1.2.3. 4') -- Traceback (most recent call last): -- VdtValueError: the value "1.2.3. 4" is unacceptable. -- >>> vtor.check('ip_addr', 0) -- Traceback (most recent call last): -- VdtTypeError: the value "0" is of the wrong type. -- """ -- if not isinstance(value, StringTypes): -- raise VdtTypeError(value) -- value = value.strip() -- try: -- dottedQuadToNum(value) -- except ValueError: -- raise VdtValueError(value) -- return value -- -- --def is_list(value, min=None, max=None): -- """ -- Check that the value is a list of values. -- -- You can optionally specify the minimum and maximum number of members. -- -- It does no check on list members. -- -- >>> vtor.check('list', ()) -- [] -- >>> vtor.check('list', []) -- [] -- >>> vtor.check('list', (1, 2)) -- [1, 2] -- >>> vtor.check('list', [1, 2]) -- [1, 2] -- >>> vtor.check('list(3)', (1, 2)) -- Traceback (most recent call last): -- VdtValueTooShortError: the value "(1, 2)" is too short. -- >>> vtor.check('list(max=5)', (1, 2, 3, 4, 5, 6)) -- Traceback (most recent call last): -- VdtValueTooLongError: the value "(1, 2, 3, 4, 5, 6)" is too long. -- >>> vtor.check('list(min=3, max=5)', (1, 2, 3, 4)) -- [1, 2, 3, 4] -- >>> vtor.check('list', 0) -- Traceback (most recent call last): -- VdtTypeError: the value "0" is of the wrong type. -- >>> vtor.check('list', '12') -- Traceback (most recent call last): -- VdtTypeError: the value "12" is of the wrong type. -- """ -- (min_len, max_len) = _is_num_param(('min', 'max'), (min, max)) -- if isinstance(value, StringTypes): -- raise VdtTypeError(value) -- try: -- num_members = len(value) -- except TypeError: -- raise VdtTypeError(value) -- if min_len is not None and num_members < min_len: -- raise VdtValueTooShortError(value) -- if max_len is not None and num_members > max_len: -- raise VdtValueTooLongError(value) -- return list(value) -- -- --def is_tuple(value, min=None, max=None): -- """ -- Check that the value is a tuple of values. -- -- You can optionally specify the minimum and maximum number of members. -- -- It does no check on members. -- -- >>> vtor.check('tuple', ()) -- () -- >>> vtor.check('tuple', []) -- () -- >>> vtor.check('tuple', (1, 2)) -- (1, 2) -- >>> vtor.check('tuple', [1, 2]) -- (1, 2) -- >>> vtor.check('tuple(3)', (1, 2)) -- Traceback (most recent call last): -- VdtValueTooShortError: the value "(1, 2)" is too short. -- >>> vtor.check('tuple(max=5)', (1, 2, 3, 4, 5, 6)) -- Traceback (most recent call last): -- VdtValueTooLongError: the value "(1, 2, 3, 4, 5, 6)" is too long. -- >>> vtor.check('tuple(min=3, max=5)', (1, 2, 3, 4)) -- (1, 2, 3, 4) -- >>> vtor.check('tuple', 0) -- Traceback (most recent call last): -- VdtTypeError: the value "0" is of the wrong type. -- >>> vtor.check('tuple', '12') -- Traceback (most recent call last): -- VdtTypeError: the value "12" is of the wrong type. -- """ -- return tuple(is_list(value, min, max)) -- -- --def is_string(value, min=None, max=None): -- """ -- Check that the supplied value is a string. -- -- You can optionally specify the minimum and maximum number of members. -- -- >>> vtor.check('string', '0') -- '0' -- >>> vtor.check('string', 0) -- Traceback (most recent call last): -- VdtTypeError: the value "0" is of the wrong type. -- >>> vtor.check('string(2)', '12') -- '12' -- >>> vtor.check('string(2)', '1') -- Traceback (most recent call last): -- VdtValueTooShortError: the value "1" is too short. -- >>> vtor.check('string(min=2, max=3)', '123') -- '123' -- >>> vtor.check('string(min=2, max=3)', '1234') -- Traceback (most recent call last): -- VdtValueTooLongError: the value "1234" is too long. -- """ -- if not isinstance(value, StringTypes): -- raise VdtTypeError(value) -- (min_len, max_len) = _is_num_param(('min', 'max'), (min, max)) -- try: -- num_members = len(value) -- except TypeError: -- raise VdtTypeError(value) -- if min_len is not None and num_members < min_len: -- raise VdtValueTooShortError(value) -- if max_len is not None and num_members > max_len: -- raise VdtValueTooLongError(value) -- return value -- -- --def is_int_list(value, min=None, max=None): -- """ -- Check that the value is a list of integers. -- -- You can optionally specify the minimum and maximum number of members. -- -- Each list member is checked that it is an integer. -- -- >>> vtor.check('int_list', ()) -- [] -- >>> vtor.check('int_list', []) -- [] -- >>> vtor.check('int_list', (1, 2)) -- [1, 2] -- >>> vtor.check('int_list', [1, 2]) -- [1, 2] -- >>> vtor.check('int_list', [1, 'a']) -- Traceback (most recent call last): -- VdtTypeError: the value "a" is of the wrong type. -- """ -- return [is_integer(mem) for mem in is_list(value, min, max)] -- -- --def is_bool_list(value, min=None, max=None): -- """ -- Check that the value is a list of booleans. -- -- You can optionally specify the minimum and maximum number of members. -- -- Each list member is checked that it is a boolean. -- -- >>> vtor.check('bool_list', ()) -- [] -- >>> vtor.check('bool_list', []) -- [] -- >>> check_res = vtor.check('bool_list', (True, False)) -- >>> check_res == [True, False] -- 1 -- >>> check_res = vtor.check('bool_list', [True, False]) -- >>> check_res == [True, False] -- 1 -- >>> vtor.check('bool_list', [True, 'a']) -- Traceback (most recent call last): -- VdtTypeError: the value "a" is of the wrong type. -- """ -- return [is_boolean(mem) for mem in is_list(value, min, max)] -- -- --def is_float_list(value, min=None, max=None): -- """ -- Check that the value is a list of floats. -- -- You can optionally specify the minimum and maximum number of members. -- -- Each list member is checked that it is a float. -- -- >>> vtor.check('float_list', ()) -- [] -- >>> vtor.check('float_list', []) -- [] -- >>> vtor.check('float_list', (1, 2.0)) -- [1.0, 2.0] -- >>> vtor.check('float_list', [1, 2.0]) -- [1.0, 2.0] -- >>> vtor.check('float_list', [1, 'a']) -- Traceback (most recent call last): -- VdtTypeError: the value "a" is of the wrong type. -- """ -- return [is_float(mem) for mem in is_list(value, min, max)] -- -- --def is_string_list(value, min=None, max=None): -- """ -- Check that the value is a list of strings. -- -- You can optionally specify the minimum and maximum number of members. -- -- Each list member is checked that it is a string. -- -- >>> vtor.check('string_list', ()) -- [] -- >>> vtor.check('string_list', []) -- [] -- >>> vtor.check('string_list', ('a', 'b')) -- ['a', 'b'] -- >>> vtor.check('string_list', ['a', 1]) -- Traceback (most recent call last): -- VdtTypeError: the value "1" is of the wrong type. -- >>> vtor.check('string_list', 'hello') -- Traceback (most recent call last): -- VdtTypeError: the value "hello" is of the wrong type. -- """ -- if isinstance(value, StringTypes): -- raise VdtTypeError(value) -- return [is_string(mem) for mem in is_list(value, min, max)] -- -- --def is_ip_addr_list(value, min=None, max=None): -- """ -- Check that the value is a list of IP addresses. -- -- You can optionally specify the minimum and maximum number of members. -- -- Each list member is checked that it is an IP address. -- -- >>> vtor.check('ip_addr_list', ()) -- [] -- >>> vtor.check('ip_addr_list', []) -- [] -- >>> vtor.check('ip_addr_list', ('1.2.3.4', '5.6.7.8')) -- ['1.2.3.4', '5.6.7.8'] -- >>> vtor.check('ip_addr_list', ['a']) -- Traceback (most recent call last): -- VdtValueError: the value "a" is unacceptable. -- """ -- return [is_ip_addr(mem) for mem in is_list(value, min, max)] -- -- --fun_dict = { -- 'integer': is_integer, -- 'float': is_float, -- 'ip_addr': is_ip_addr, -- 'string': is_string, -- 'boolean': is_boolean, --} -- -- --def is_mixed_list(value, *args): -- """ -- Check that the value is a list. -- Allow specifying the type of each member. -- Work on lists of specific lengths. -- -- You specify each member as a positional argument specifying type -- -- Each type should be one of the following strings : -- 'integer', 'float', 'ip_addr', 'string', 'boolean' -- -- So you can specify a list of two strings, followed by -- two integers as : -- -- mixed_list('string', 'string', 'integer', 'integer') -- -- The length of the list must match the number of positional -- arguments you supply. -- -- >>> mix_str = "mixed_list('integer', 'float', 'ip_addr', 'string', 'boolean')" -- >>> check_res = vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', True)) -- >>> check_res == [1, 2.0, '1.2.3.4', 'a', True] -- 1 -- >>> check_res = vtor.check(mix_str, ('1', '2.0', '1.2.3.4', 'a', 'True')) -- >>> check_res == [1, 2.0, '1.2.3.4', 'a', True] -- 1 -- >>> vtor.check(mix_str, ('b', 2.0, '1.2.3.4', 'a', True)) -- Traceback (most recent call last): -- VdtTypeError: the value "b" is of the wrong type. -- >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a')) -- Traceback (most recent call last): -- VdtValueTooShortError: the value "(1, 2.0, '1.2.3.4', 'a')" is too short. -- >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', 1, 'b')) -- Traceback (most recent call last): -- VdtValueTooLongError: the value "(1, 2.0, '1.2.3.4', 'a', 1, 'b')" is too long. -- >>> vtor.check(mix_str, 0) -- Traceback (most recent call last): -- VdtTypeError: the value "0" is of the wrong type. -- -- This test requires an elaborate setup, because of a change in error string -- output from the interpreter between Python 2.2 and 2.3 . -- -- >>> res_seq = ( -- ... 'passed an incorrect value "', -- ... 'yoda', -- ... '" for parameter "mixed_list".', -- ... ) -- >>> if INTP_VER == (2, 2): -- ... res_str = "".join(res_seq) -- ... else: -- ... res_str = "'".join(res_seq) -- >>> try: -- ... vtor.check('mixed_list("yoda")', ('a')) -- ... except VdtParamError, err: -- ... str(err) == res_str -- 1 -- """ -- try: -- length = len(value) -- except TypeError: -- raise VdtTypeError(value) -- if length < len(args): -- raise VdtValueTooShortError(value) -- elif length > len(args): -- raise VdtValueTooLongError(value) -- try: -- return [fun_dict[arg](val) for arg, val in zip(args, value)] -- except KeyError, e: -- raise VdtParamError('mixed_list', e) -- -- --def is_option(value, *options): -- """ -- This check matches the value to any of a set of options. -- -- >>> vtor.check('option("yoda", "jedi")', 'yoda') -- 'yoda' -- >>> vtor.check('option("yoda", "jedi")', 'jed') -- Traceback (most recent call last): -- VdtValueError: the value "jed" is unacceptable. -- >>> vtor.check('option("yoda", "jedi")', 0) -- Traceback (most recent call last): -- VdtTypeError: the value "0" is of the wrong type. -- """ -- if not isinstance(value, StringTypes): -- raise VdtTypeError(value) -- if not value in options: -- raise VdtValueError(value) -- return value -- -- --def _test(value, *args, **keywargs): -- """ -- A function that exists for test purposes. -- -- >>> checks = [ -- ... '3, 6, min=1, max=3, test=list(a, b, c)', -- ... '3', -- ... '3, 6', -- ... '3,', -- ... 'min=1, test="a b c"', -- ... 'min=5, test="a, b, c"', -- ... 'min=1, max=3, test="a, b, c"', -- ... 'min=-100, test=-99', -- ... 'min=1, max=3', -- ... '3, 6, test="36"', -- ... '3, 6, test="a, b, c"', -- ... '3, max=3, test=list("a", "b", "c")', -- ... '''3, max=3, test=list("'a'", 'b', "x=(c)")''', -- ... "test='x=fish(3)'", -- ... ] -- >>> v = Validator({'test': _test}) -- >>> for entry in checks: -- ... print v.check(('test(%s)' % entry), 3) -- (3, ('3', '6'), {'test': ['a', 'b', 'c'], 'max': '3', 'min': '1'}) -- (3, ('3',), {}) -- (3, ('3', '6'), {}) -- (3, ('3',), {}) -- (3, (), {'test': 'a b c', 'min': '1'}) -- (3, (), {'test': 'a, b, c', 'min': '5'}) -- (3, (), {'test': 'a, b, c', 'max': '3', 'min': '1'}) -- (3, (), {'test': '-99', 'min': '-100'}) -- (3, (), {'max': '3', 'min': '1'}) -- (3, ('3', '6'), {'test': '36'}) -- (3, ('3', '6'), {'test': 'a, b, c'}) -- (3, ('3',), {'test': ['a', 'b', 'c'], 'max': '3'}) -- (3, ('3',), {'test': ["'a'", 'b', 'x=(c)'], 'max': '3'}) -- (3, (), {'test': 'x=fish(3)'}) -- -- >>> v = Validator() -- >>> v.check('integer(default=6)', '3') -- 3 -- >>> v.check('integer(default=6)', None, True) -- 6 -- >>> v.get_default_value('integer(default=6)') -- 6 -- >>> v.get_default_value('float(default=6)') -- 6.0 -- >>> v.get_default_value('pass(default=None)') -- >>> v.get_default_value("string(default='None')") -- 'None' -- >>> v.get_default_value('pass') -- Traceback (most recent call last): -- KeyError: 'Check "pass" has no default value.' -- >>> v.get_default_value('pass(default=list(1, 2, 3, 4))') -- ['1', '2', '3', '4'] -- -- >>> v = Validator() -- >>> v.check("pass(default=None)", None, True) -- >>> v.check("pass(default='None')", None, True) -- 'None' -- >>> v.check('pass(default="None")', None, True) -- 'None' -- >>> v.check('pass(default=list(1, 2, 3, 4))', None, True) -- ['1', '2', '3', '4'] -- -- Bug test for unicode arguments -- >>> v = Validator() -- >>> v.check(u'string(min=4)', u'test') -- u'test' -- -- >>> v = Validator() -- >>> v.get_default_value(u'string(min=4, default="1234")') -- u'1234' -- >>> v.check(u'string(min=4, default="1234")', u'test') -- u'test' -- -- >>> v = Validator() -- >>> default = v.get_default_value('string(default=None)') -- >>> default == None -- 1 -- """ -- return (value, args, keywargs) -- -- --if __name__ == '__main__': -- # run the code tests in doctest format -- import doctest -- m = sys.modules.get('__main__') -- globs = m.__dict__.copy() -- globs.update({ -- 'INTP_VER': INTP_VER, -- 'vtor': Validator(), -- }) -- doctest.testmod(m, globs=globs) -Index: ipython-0.10/IPython/external/validate/_validate.py -=================================================================== ---- /dev/null -+++ ipython-0.10/IPython/external/validate/_validate.py -@@ -0,0 +1,1414 @@ -+# validate.py -+# A Validator object -+# Copyright (C) 2005 Michael Foord, Mark Andrews, Nicola Larosa -+# E-mail: fuzzyman AT voidspace DOT org DOT uk -+# mark AT la-la DOT com -+# nico AT tekNico DOT net -+ -+# This software is licensed under the terms of the BSD license. -+# http://www.voidspace.org.uk/python/license.shtml -+# Basically you're free to copy, modify, distribute and relicense it, -+# So long as you keep a copy of the license with it. -+ -+# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml -+# For information about bugfixes, updates and support, please join the -+# ConfigObj mailing list: -+# http://lists.sourceforge.net/lists/listinfo/configobj-develop -+# Comments, suggestions and bug reports welcome. -+ -+""" -+ The Validator object is used to check that supplied values -+ conform to a specification. -+ -+ The value can be supplied as a string - e.g. from a config file. -+ In this case the check will also *convert* the value to -+ the required type. This allows you to add validation -+ as a transparent layer to access data stored as strings. -+ The validation checks that the data is correct *and* -+ converts it to the expected type. -+ -+ Some standard checks are provided for basic data types. -+ Additional checks are easy to write. They can be -+ provided when the ``Validator`` is instantiated or -+ added afterwards. -+ -+ The standard functions work with the following basic data types : -+ -+ * integers -+ * floats -+ * booleans -+ * strings -+ * ip_addr -+ -+ plus lists of these datatypes -+ -+ Adding additional checks is done through coding simple functions. -+ -+ The full set of standard checks are : -+ -+ * 'integer': matches integer values (including negative) -+ Takes optional 'min' and 'max' arguments : :: -+ -+ integer() -+ integer(3, 9) # any value from 3 to 9 -+ integer(min=0) # any positive value -+ integer(max=9) -+ -+ * 'float': matches float values -+ Has the same parameters as the integer check. -+ -+ * 'boolean': matches boolean values - ``True`` or ``False`` -+ Acceptable string values for True are : -+ true, on, yes, 1 -+ Acceptable string values for False are : -+ false, off, no, 0 -+ -+ Any other value raises an error. -+ -+ * 'ip_addr': matches an Internet Protocol address, v.4, represented -+ by a dotted-quad string, i.e. '1.2.3.4'. -+ -+ * 'string': matches any string. -+ Takes optional keyword args 'min' and 'max' -+ to specify min and max lengths of the string. -+ -+ * 'list': matches any list. -+ Takes optional keyword args 'min', and 'max' to specify min and -+ max sizes of the list. (Always returns a list.) -+ -+ * 'tuple': matches any tuple. -+ Takes optional keyword args 'min', and 'max' to specify min and -+ max sizes of the tuple. (Always returns a tuple.) -+ -+ * 'int_list': Matches a list of integers. -+ Takes the same arguments as list. -+ -+ * 'float_list': Matches a list of floats. -+ Takes the same arguments as list. -+ -+ * 'bool_list': Matches a list of boolean values. -+ Takes the same arguments as list. -+ -+ * 'ip_addr_list': Matches a list of IP addresses. -+ Takes the same arguments as list. -+ -+ * 'string_list': Matches a list of strings. -+ Takes the same arguments as list. -+ -+ * 'mixed_list': Matches a list with different types in -+ specific positions. List size must match -+ the number of arguments. -+ -+ Each position can be one of : -+ 'integer', 'float', 'ip_addr', 'string', 'boolean' -+ -+ So to specify a list with two strings followed -+ by two integers, you write the check as : :: -+ -+ mixed_list('string', 'string', 'integer', 'integer') -+ -+ * 'pass': This check matches everything ! It never fails -+ and the value is unchanged. -+ -+ It is also the default if no check is specified. -+ -+ * 'option': This check matches any from a list of options. -+ You specify this check with : :: -+ -+ option('option 1', 'option 2', 'option 3') -+ -+ You can supply a default value (returned if no value is supplied) -+ using the default keyword argument. -+ -+ You specify a list argument for default using a list constructor syntax in -+ the check : :: -+ -+ checkname(arg1, arg2, default=list('val 1', 'val 2', 'val 3')) -+ -+ A badly formatted set of arguments will raise a ``VdtParamError``. -+""" -+ -+__docformat__ = "restructuredtext en" -+ -+__version__ = '0.3.2' -+ -+__revision__ = '$Id: validate.py 123 2005-09-08 08:54:28Z fuzzyman $' -+ -+__all__ = ( -+ '__version__', -+ 'dottedQuadToNum', -+ 'numToDottedQuad', -+ 'ValidateError', -+ 'VdtUnknownCheckError', -+ 'VdtParamError', -+ 'VdtTypeError', -+ 'VdtValueError', -+ 'VdtValueTooSmallError', -+ 'VdtValueTooBigError', -+ 'VdtValueTooShortError', -+ 'VdtValueTooLongError', -+ 'VdtMissingValue', -+ 'Validator', -+ 'is_integer', -+ 'is_float', -+ 'is_boolean', -+ 'is_list', -+ 'is_tuple', -+ 'is_ip_addr', -+ 'is_string', -+ 'is_int_list', -+ 'is_bool_list', -+ 'is_float_list', -+ 'is_string_list', -+ 'is_ip_addr_list', -+ 'is_mixed_list', -+ 'is_option', -+ '__docformat__', -+) -+ -+ -+import sys -+INTP_VER = sys.version_info[:2] -+if INTP_VER < (2, 2): -+ raise RuntimeError("Python v.2.2 or later needed") -+ -+import re -+StringTypes = (str, unicode) -+ -+ -+_list_arg = re.compile(r''' -+ (?: -+ ([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*list\( -+ ( -+ (?: -+ \s* -+ (?: -+ (?:".*?")| # double quotes -+ (?:'.*?')| # single quotes -+ (?:[^'",\s\)][^,\)]*?) # unquoted -+ ) -+ \s*,\s* -+ )* -+ (?: -+ (?:".*?")| # double quotes -+ (?:'.*?')| # single quotes -+ (?:[^'",\s\)][^,\)]*?) # unquoted -+ )? # last one -+ ) -+ \) -+ ) -+''', re.VERBOSE) # two groups -+ -+_list_members = re.compile(r''' -+ ( -+ (?:".*?")| # double quotes -+ (?:'.*?')| # single quotes -+ (?:[^'",\s=][^,=]*?) # unquoted -+ ) -+ (?: -+ (?:\s*,\s*)|(?:\s*$) # comma -+ ) -+''', re.VERBOSE) # one group -+ -+_paramstring = r''' -+ (?: -+ ( -+ (?: -+ [a-zA-Z_][a-zA-Z0-9_]*\s*=\s*list\( -+ (?: -+ \s* -+ (?: -+ (?:".*?")| # double quotes -+ (?:'.*?')| # single quotes -+ (?:[^'",\s\)][^,\)]*?) # unquoted -+ ) -+ \s*,\s* -+ )* -+ (?: -+ (?:".*?")| # double quotes -+ (?:'.*?')| # single quotes -+ (?:[^'",\s\)][^,\)]*?) # unquoted -+ )? # last one -+ \) -+ )| -+ (?: -+ (?:".*?")| # double quotes -+ (?:'.*?')| # single quotes -+ (?:[^'",\s=][^,=]*?)| # unquoted -+ (?: # keyword argument -+ [a-zA-Z_][a-zA-Z0-9_]*\s*=\s* -+ (?: -+ (?:".*?")| # double quotes -+ (?:'.*?')| # single quotes -+ (?:[^'",\s=][^,=]*?) # unquoted -+ ) -+ ) -+ ) -+ ) -+ (?: -+ (?:\s*,\s*)|(?:\s*$) # comma -+ ) -+ ) -+ ''' -+ -+_matchstring = '^%s*' % _paramstring -+ -+# Python pre 2.2.1 doesn't have bool -+try: -+ bool -+except NameError: -+ def bool(val): -+ """Simple boolean equivalent function. """ -+ if val: -+ return 1 -+ else: -+ return 0 -+ -+ -+def dottedQuadToNum(ip): -+ """ -+ Convert decimal dotted quad string to long integer -+ -+ >>> dottedQuadToNum('1 ') -+ 1L -+ >>> dottedQuadToNum(' 1.2') -+ 16777218L -+ >>> dottedQuadToNum(' 1.2.3 ') -+ 16908291L -+ >>> dottedQuadToNum('1.2.3.4') -+ 16909060L -+ >>> dottedQuadToNum('1.2.3. 4') -+ Traceback (most recent call last): -+ ValueError: Not a good dotted-quad IP: 1.2.3. 4 -+ >>> dottedQuadToNum('255.255.255.255') -+ 4294967295L -+ >>> dottedQuadToNum('255.255.255.256') -+ Traceback (most recent call last): -+ ValueError: Not a good dotted-quad IP: 255.255.255.256 -+ """ -+ -+ # import here to avoid it when ip_addr values are not used -+ import socket, struct -+ -+ try: -+ return struct.unpack('!L', -+ socket.inet_aton(ip.strip()))[0] -+ except socket.error: -+ # bug in inet_aton, corrected in Python 2.3 -+ if ip.strip() == '255.255.255.255': -+ return 0xFFFFFFFFL -+ else: -+ raise ValueError('Not a good dotted-quad IP: %s' % ip) -+ return -+ -+ -+def numToDottedQuad(num): -+ """ -+ Convert long int to dotted quad string -+ -+ >>> numToDottedQuad(-1L) -+ Traceback (most recent call last): -+ ValueError: Not a good numeric IP: -1 -+ >>> numToDottedQuad(1L) -+ '0.0.0.1' -+ >>> numToDottedQuad(16777218L) -+ '1.0.0.2' -+ >>> numToDottedQuad(16908291L) -+ '1.2.0.3' -+ >>> numToDottedQuad(16909060L) -+ '1.2.3.4' -+ >>> numToDottedQuad(4294967295L) -+ '255.255.255.255' -+ >>> numToDottedQuad(4294967296L) -+ Traceback (most recent call last): -+ ValueError: Not a good numeric IP: 4294967296 -+ """ -+ -+ # import here to avoid it when ip_addr values are not used -+ import socket, struct -+ -+ # no need to intercept here, 4294967295L is fine -+ try: -+ return socket.inet_ntoa( -+ struct.pack('!L', long(num))) -+ except (socket.error, struct.error, OverflowError): -+ raise ValueError('Not a good numeric IP: %s' % num) -+ -+ -+class ValidateError(Exception): -+ """ -+ This error indicates that the check failed. -+ It can be the base class for more specific errors. -+ -+ Any check function that fails ought to raise this error. -+ (or a subclass) -+ -+ >>> raise ValidateError -+ Traceback (most recent call last): -+ ValidateError -+ """ -+ -+ -+class VdtMissingValue(ValidateError): -+ """No value was supplied to a check that needed one.""" -+ -+ -+class VdtUnknownCheckError(ValidateError): -+ """An unknown check function was requested""" -+ -+ def __init__(self, value): -+ """ -+ >>> raise VdtUnknownCheckError('yoda') -+ Traceback (most recent call last): -+ VdtUnknownCheckError: the check "yoda" is unknown. -+ """ -+ ValidateError.__init__(self, 'the check "%s" is unknown.' % (value,)) -+ -+ -+class VdtParamError(SyntaxError): -+ """An incorrect parameter was passed""" -+ -+ def __init__(self, name, value): -+ """ -+ >>> raise VdtParamError('yoda', 'jedi') -+ Traceback (most recent call last): -+ VdtParamError: passed an incorrect value "jedi" for parameter "yoda". -+ """ -+ SyntaxError.__init__(self, 'passed an incorrect value "%s" for parameter "%s".' % (value, name)) -+ -+ -+class VdtTypeError(ValidateError): -+ """The value supplied was of the wrong type""" -+ -+ def __init__(self, value): -+ """ -+ >>> raise VdtTypeError('jedi') -+ Traceback (most recent call last): -+ VdtTypeError: the value "jedi" is of the wrong type. -+ """ -+ ValidateError.__init__(self, 'the value "%s" is of the wrong type.' % (value,)) -+ -+ -+class VdtValueError(ValidateError): -+ """The value supplied was of the correct type, but was not an allowed value.""" -+ -+ def __init__(self, value): -+ """ -+ >>> raise VdtValueError('jedi') -+ Traceback (most recent call last): -+ VdtValueError: the value "jedi" is unacceptable. -+ """ -+ ValidateError.__init__(self, 'the value "%s" is unacceptable.' % (value,)) -+ -+ -+class VdtValueTooSmallError(VdtValueError): -+ """The value supplied was of the correct type, but was too small.""" -+ -+ def __init__(self, value): -+ """ -+ >>> raise VdtValueTooSmallError('0') -+ Traceback (most recent call last): -+ VdtValueTooSmallError: the value "0" is too small. -+ """ -+ ValidateError.__init__(self, 'the value "%s" is too small.' % (value,)) -+ -+ -+class VdtValueTooBigError(VdtValueError): -+ """The value supplied was of the correct type, but was too big.""" -+ -+ def __init__(self, value): -+ """ -+ >>> raise VdtValueTooBigError('1') -+ Traceback (most recent call last): -+ VdtValueTooBigError: the value "1" is too big. -+ """ -+ ValidateError.__init__(self, 'the value "%s" is too big.' % (value,)) -+ -+ -+class VdtValueTooShortError(VdtValueError): -+ """The value supplied was of the correct type, but was too short.""" -+ -+ def __init__(self, value): -+ """ -+ >>> raise VdtValueTooShortError('jed') -+ Traceback (most recent call last): -+ VdtValueTooShortError: the value "jed" is too short. -+ """ -+ ValidateError.__init__( -+ self, -+ 'the value "%s" is too short.' % (value,)) -+ -+ -+class VdtValueTooLongError(VdtValueError): -+ """The value supplied was of the correct type, but was too long.""" -+ -+ def __init__(self, value): -+ """ -+ >>> raise VdtValueTooLongError('jedie') -+ Traceback (most recent call last): -+ VdtValueTooLongError: the value "jedie" is too long. -+ """ -+ ValidateError.__init__(self, 'the value "%s" is too long.' % (value,)) -+ -+ -+class Validator(object): -+ """ -+ Validator is an object that allows you to register a set of 'checks'. -+ These checks take input and test that it conforms to the check. -+ -+ This can also involve converting the value from a string into -+ the correct datatype. -+ -+ The ``check`` method takes an input string which configures which -+ check is to be used and applies that check to a supplied value. -+ -+ An example input string would be: -+ 'int_range(param1, param2)' -+ -+ You would then provide something like: -+ -+ >>> def int_range_check(value, min, max): -+ ... # turn min and max from strings to integers -+ ... min = int(min) -+ ... max = int(max) -+ ... # check that value is of the correct type. -+ ... # possible valid inputs are integers or strings -+ ... # that represent integers -+ ... if not isinstance(value, (int, long, StringTypes)): -+ ... raise VdtTypeError(value) -+ ... elif isinstance(value, StringTypes): -+ ... # if we are given a string -+ ... # attempt to convert to an integer -+ ... try: -+ ... value = int(value) -+ ... except ValueError: -+ ... raise VdtValueError(value) -+ ... # check the value is between our constraints -+ ... if not min <= value: -+ ... raise VdtValueTooSmallError(value) -+ ... if not value <= max: -+ ... raise VdtValueTooBigError(value) -+ ... return value -+ -+ >>> fdict = {'int_range': int_range_check} -+ >>> vtr1 = Validator(fdict) -+ >>> vtr1.check('int_range(20, 40)', '30') -+ 30 -+ >>> vtr1.check('int_range(20, 40)', '60') -+ Traceback (most recent call last): -+ VdtValueTooBigError: the value "60" is too big. -+ -+ New functions can be added with : :: -+ -+ >>> vtr2 = Validator() -+ >>> vtr2.functions['int_range'] = int_range_check -+ -+ Or by passing in a dictionary of functions when Validator -+ is instantiated. -+ -+ Your functions *can* use keyword arguments, -+ but the first argument should always be 'value'. -+ -+ If the function doesn't take additional arguments, -+ the parentheses are optional in the check. -+ It can be written with either of : :: -+ -+ keyword = function_name -+ keyword = function_name() -+ -+ The first program to utilise Validator() was Michael Foord's -+ ConfigObj, an alternative to ConfigParser which supports lists and -+ can validate a config file using a config schema. -+ For more details on using Validator with ConfigObj see: -+ http://www.voidspace.org.uk/python/configobj.html -+ """ -+ -+ # this regex does the initial parsing of the checks -+ _func_re = re.compile(r'(.+?)\((.*)\)') -+ -+ # this regex takes apart keyword arguments -+ _key_arg = re.compile(r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.*)$') -+ -+ -+ # this regex finds keyword=list(....) type values -+ _list_arg = _list_arg -+ -+ # this regex takes individual values out of lists - in one pass -+ _list_members = _list_members -+ -+ # These regexes check a set of arguments for validity -+ # and then pull the members out -+ _paramfinder = re.compile(_paramstring, re.VERBOSE) -+ _matchfinder = re.compile(_matchstring, re.VERBOSE) -+ -+ -+ def __init__(self, functions=None): -+ """ -+ >>> vtri = Validator() -+ """ -+ self.functions = { -+ '': self._pass, -+ 'integer': is_integer, -+ 'float': is_float, -+ 'boolean': is_boolean, -+ 'ip_addr': is_ip_addr, -+ 'string': is_string, -+ 'list': is_list, -+ 'tuple': is_tuple, -+ 'int_list': is_int_list, -+ 'float_list': is_float_list, -+ 'bool_list': is_bool_list, -+ 'ip_addr_list': is_ip_addr_list, -+ 'string_list': is_string_list, -+ 'mixed_list': is_mixed_list, -+ 'pass': self._pass, -+ 'option': is_option, -+ } -+ if functions is not None: -+ self.functions.update(functions) -+ # tekNico: for use by ConfigObj -+ self.baseErrorClass = ValidateError -+ self._cache = {} -+ -+ -+ def check(self, check, value, missing=False): -+ """ -+ Usage: check(check, value) -+ -+ Arguments: -+ check: string representing check to apply (including arguments) -+ value: object to be checked -+ Returns value, converted to correct type if necessary -+ -+ If the check fails, raises a ``ValidateError`` subclass. -+ -+ >>> vtor.check('yoda', '') -+ Traceback (most recent call last): -+ VdtUnknownCheckError: the check "yoda" is unknown. -+ >>> vtor.check('yoda()', '') -+ Traceback (most recent call last): -+ VdtUnknownCheckError: the check "yoda" is unknown. -+ -+ >>> vtor.check('string(default="")', '', missing=True) -+ '' -+ """ -+ fun_name, fun_args, fun_kwargs, default = self._parse_with_caching(check) -+ -+ if missing: -+ if default is None: -+ # no information needed here - to be handled by caller -+ raise VdtMissingValue() -+ value = self._handle_none(default) -+ -+ if value is None: -+ return None -+ -+ return self._check_value(value, fun_name, fun_args, fun_kwargs) -+ -+ -+ def _handle_none(self, value): -+ if value == 'None': -+ value = None -+ elif value in ("'None'", '"None"'): -+ # Special case a quoted None -+ value = self._unquote(value) -+ return value -+ -+ -+ def _parse_with_caching(self, check): -+ if check in self._cache: -+ fun_name, fun_args, fun_kwargs, default = self._cache[check] -+ # We call list and dict below to work with *copies* of the data -+ # rather than the original (which are mutable of course) -+ fun_args = list(fun_args) -+ fun_kwargs = dict(fun_kwargs) -+ else: -+ fun_name, fun_args, fun_kwargs, default = self._parse_check(check) -+ fun_kwargs = dict((str(key), value) for (key, value) in fun_kwargs.items()) -+ self._cache[check] = fun_name, list(fun_args), dict(fun_kwargs), default -+ return fun_name, fun_args, fun_kwargs, default -+ -+ -+ def _check_value(self, value, fun_name, fun_args, fun_kwargs): -+ try: -+ fun = self.functions[fun_name] -+ except KeyError: -+ raise VdtUnknownCheckError(fun_name) -+ else: -+ return fun(value, *fun_args, **fun_kwargs) -+ -+ -+ def _parse_check(self, check): -+ fun_match = self._func_re.match(check) -+ if fun_match: -+ fun_name = fun_match.group(1) -+ arg_string = fun_match.group(2) -+ arg_match = self._matchfinder.match(arg_string) -+ if arg_match is None: -+ # Bad syntax -+ raise VdtParamError('Bad syntax in check "%s".' % check) -+ fun_args = [] -+ fun_kwargs = {} -+ # pull out args of group 2 -+ for arg in self._paramfinder.findall(arg_string): -+ # args may need whitespace removing (before removing quotes) -+ arg = arg.strip() -+ listmatch = self._list_arg.match(arg) -+ if listmatch: -+ key, val = self._list_handle(listmatch) -+ fun_kwargs[key] = val -+ continue -+ keymatch = self._key_arg.match(arg) -+ if keymatch: -+ val = keymatch.group(2) -+ if not val in ("'None'", '"None"'): -+ # Special case a quoted None -+ val = self._unquote(val) -+ fun_kwargs[keymatch.group(1)] = val -+ continue -+ -+ fun_args.append(self._unquote(arg)) -+ else: -+ # allows for function names without (args) -+ return check, (), {}, None -+ -+ # Default must be deleted if the value is specified too, -+ # otherwise the check function will get a spurious "default" keyword arg -+ try: -+ default = fun_kwargs.pop('default', None) -+ except AttributeError: -+ # Python 2.2 compatibility -+ default = None -+ try: -+ default = fun_kwargs['default'] -+ del fun_kwargs['default'] -+ except KeyError: -+ pass -+ -+ return fun_name, fun_args, fun_kwargs, default -+ -+ -+ def _unquote(self, val): -+ """Unquote a value if necessary.""" -+ if (len(val) >= 2) and (val[0] in ("'", '"')) and (val[0] == val[-1]): -+ val = val[1:-1] -+ return val -+ -+ -+ def _list_handle(self, listmatch): -+ """Take apart a ``keyword=list('val, 'val')`` type string.""" -+ out = [] -+ name = listmatch.group(1) -+ args = listmatch.group(2) -+ for arg in self._list_members.findall(args): -+ out.append(self._unquote(arg)) -+ return name, out -+ -+ -+ def _pass(self, value): -+ """ -+ Dummy check that always passes -+ -+ >>> vtor.check('', 0) -+ 0 -+ >>> vtor.check('', '0') -+ '0' -+ """ -+ return value -+ -+ -+ def get_default_value(self, check): -+ """ -+ Given a check, return the default value for the check -+ (converted to the right type). -+ -+ If the check doesn't specify a default value then a -+ ``KeyError`` will be raised. -+ """ -+ fun_name, fun_args, fun_kwargs, default = self._parse_with_caching(check) -+ if default is None: -+ raise KeyError('Check "%s" has no default value.' % check) -+ value = self._handle_none(default) -+ if value is None: -+ return value -+ return self._check_value(value, fun_name, fun_args, fun_kwargs) -+ -+ -+def _is_num_param(names, values, to_float=False): -+ """ -+ Return numbers from inputs or raise VdtParamError. -+ -+ Lets ``None`` pass through. -+ Pass in keyword argument ``to_float=True`` to -+ use float for the conversion rather than int. -+ -+ >>> _is_num_param(('', ''), (0, 1.0)) -+ [0, 1] -+ >>> _is_num_param(('', ''), (0, 1.0), to_float=True) -+ [0.0, 1.0] -+ >>> _is_num_param(('a'), ('a')) -+ Traceback (most recent call last): -+ VdtParamError: passed an incorrect value "a" for parameter "a". -+ """ -+ fun = to_float and float or int -+ out_params = [] -+ for (name, val) in zip(names, values): -+ if val is None: -+ out_params.append(val) -+ elif isinstance(val, (int, long, float, StringTypes)): -+ try: -+ out_params.append(fun(val)) -+ except ValueError, e: -+ raise VdtParamError(name, val) -+ else: -+ raise VdtParamError(name, val) -+ return out_params -+ -+ -+# built in checks -+# you can override these by setting the appropriate name -+# in Validator.functions -+# note: if the params are specified wrongly in your input string, -+# you will also raise errors. -+ -+def is_integer(value, min=None, max=None): -+ """ -+ A check that tests that a given value is an integer (int, or long) -+ and optionally, between bounds. A negative value is accepted, while -+ a float will fail. -+ -+ If the value is a string, then the conversion is done - if possible. -+ Otherwise a VdtError is raised. -+ -+ >>> vtor.check('integer', '-1') -+ -1 -+ >>> vtor.check('integer', '0') -+ 0 -+ >>> vtor.check('integer', 9) -+ 9 -+ >>> vtor.check('integer', 'a') -+ Traceback (most recent call last): -+ VdtTypeError: the value "a" is of the wrong type. -+ >>> vtor.check('integer', '2.2') -+ Traceback (most recent call last): -+ VdtTypeError: the value "2.2" is of the wrong type. -+ >>> vtor.check('integer(10)', '20') -+ 20 -+ >>> vtor.check('integer(max=20)', '15') -+ 15 -+ >>> vtor.check('integer(10)', '9') -+ Traceback (most recent call last): -+ VdtValueTooSmallError: the value "9" is too small. -+ >>> vtor.check('integer(10)', 9) -+ Traceback (most recent call last): -+ VdtValueTooSmallError: the value "9" is too small. -+ >>> vtor.check('integer(max=20)', '35') -+ Traceback (most recent call last): -+ VdtValueTooBigError: the value "35" is too big. -+ >>> vtor.check('integer(max=20)', 35) -+ Traceback (most recent call last): -+ VdtValueTooBigError: the value "35" is too big. -+ >>> vtor.check('integer(0, 9)', False) -+ 0 -+ """ -+ (min_val, max_val) = _is_num_param(('min', 'max'), (min, max)) -+ if not isinstance(value, (int, long, StringTypes)): -+ raise VdtTypeError(value) -+ if isinstance(value, StringTypes): -+ # if it's a string - does it represent an integer ? -+ try: -+ value = int(value) -+ except ValueError: -+ raise VdtTypeError(value) -+ if (min_val is not None) and (value < min_val): -+ raise VdtValueTooSmallError(value) -+ if (max_val is not None) and (value > max_val): -+ raise VdtValueTooBigError(value) -+ return value -+ -+ -+def is_float(value, min=None, max=None): -+ """ -+ A check that tests that a given value is a float -+ (an integer will be accepted), and optionally - that it is between bounds. -+ -+ If the value is a string, then the conversion is done - if possible. -+ Otherwise a VdtError is raised. -+ -+ This can accept negative values. -+ -+ >>> vtor.check('float', '2') -+ 2.0 -+ -+ From now on we multiply the value to avoid comparing decimals -+ -+ >>> vtor.check('float', '-6.8') * 10 -+ -68.0 -+ >>> vtor.check('float', '12.2') * 10 -+ 122.0 -+ >>> vtor.check('float', 8.4) * 10 -+ 84.0 -+ >>> vtor.check('float', 'a') -+ Traceback (most recent call last): -+ VdtTypeError: the value "a" is of the wrong type. -+ >>> vtor.check('float(10.1)', '10.2') * 10 -+ 102.0 -+ >>> vtor.check('float(max=20.2)', '15.1') * 10 -+ 151.0 -+ >>> vtor.check('float(10.0)', '9.0') -+ Traceback (most recent call last): -+ VdtValueTooSmallError: the value "9.0" is too small. -+ >>> vtor.check('float(max=20.0)', '35.0') -+ Traceback (most recent call last): -+ VdtValueTooBigError: the value "35.0" is too big. -+ """ -+ (min_val, max_val) = _is_num_param( -+ ('min', 'max'), (min, max), to_float=True) -+ if not isinstance(value, (int, long, float, StringTypes)): -+ raise VdtTypeError(value) -+ if not isinstance(value, float): -+ # if it's a string - does it represent a float ? -+ try: -+ value = float(value) -+ except ValueError: -+ raise VdtTypeError(value) -+ if (min_val is not None) and (value < min_val): -+ raise VdtValueTooSmallError(value) -+ if (max_val is not None) and (value > max_val): -+ raise VdtValueTooBigError(value) -+ return value -+ -+ -+bool_dict = { -+ True: True, 'on': True, '1': True, 'true': True, 'yes': True, -+ False: False, 'off': False, '0': False, 'false': False, 'no': False, -+} -+ -+ -+def is_boolean(value): -+ """ -+ Check if the value represents a boolean. -+ -+ >>> vtor.check('boolean', 0) -+ 0 -+ >>> vtor.check('boolean', False) -+ 0 -+ >>> vtor.check('boolean', '0') -+ 0 -+ >>> vtor.check('boolean', 'off') -+ 0 -+ >>> vtor.check('boolean', 'false') -+ 0 -+ >>> vtor.check('boolean', 'no') -+ 0 -+ >>> vtor.check('boolean', 'nO') -+ 0 -+ >>> vtor.check('boolean', 'NO') -+ 0 -+ >>> vtor.check('boolean', 1) -+ 1 -+ >>> vtor.check('boolean', True) -+ 1 -+ >>> vtor.check('boolean', '1') -+ 1 -+ >>> vtor.check('boolean', 'on') -+ 1 -+ >>> vtor.check('boolean', 'true') -+ 1 -+ >>> vtor.check('boolean', 'yes') -+ 1 -+ >>> vtor.check('boolean', 'Yes') -+ 1 -+ >>> vtor.check('boolean', 'YES') -+ 1 -+ >>> vtor.check('boolean', '') -+ Traceback (most recent call last): -+ VdtTypeError: the value "" is of the wrong type. -+ >>> vtor.check('boolean', 'up') -+ Traceback (most recent call last): -+ VdtTypeError: the value "up" is of the wrong type. -+ -+ """ -+ if isinstance(value, StringTypes): -+ try: -+ return bool_dict[value.lower()] -+ except KeyError: -+ raise VdtTypeError(value) -+ # we do an equality test rather than an identity test -+ # this ensures Python 2.2 compatibilty -+ # and allows 0 and 1 to represent True and False -+ if value == False: -+ return False -+ elif value == True: -+ return True -+ else: -+ raise VdtTypeError(value) -+ -+ -+def is_ip_addr(value): -+ """ -+ Check that the supplied value is an Internet Protocol address, v.4, -+ represented by a dotted-quad string, i.e. '1.2.3.4'. -+ -+ >>> vtor.check('ip_addr', '1 ') -+ '1' -+ >>> vtor.check('ip_addr', ' 1.2') -+ '1.2' -+ >>> vtor.check('ip_addr', ' 1.2.3 ') -+ '1.2.3' -+ >>> vtor.check('ip_addr', '1.2.3.4') -+ '1.2.3.4' -+ >>> vtor.check('ip_addr', '0.0.0.0') -+ '0.0.0.0' -+ >>> vtor.check('ip_addr', '255.255.255.255') -+ '255.255.255.255' -+ >>> vtor.check('ip_addr', '255.255.255.256') -+ Traceback (most recent call last): -+ VdtValueError: the value "255.255.255.256" is unacceptable. -+ >>> vtor.check('ip_addr', '1.2.3.4.5') -+ Traceback (most recent call last): -+ VdtValueError: the value "1.2.3.4.5" is unacceptable. -+ >>> vtor.check('ip_addr', '1.2.3. 4') -+ Traceback (most recent call last): -+ VdtValueError: the value "1.2.3. 4" is unacceptable. -+ >>> vtor.check('ip_addr', 0) -+ Traceback (most recent call last): -+ VdtTypeError: the value "0" is of the wrong type. -+ """ -+ if not isinstance(value, StringTypes): -+ raise VdtTypeError(value) -+ value = value.strip() -+ try: -+ dottedQuadToNum(value) -+ except ValueError: -+ raise VdtValueError(value) -+ return value -+ -+ -+def is_list(value, min=None, max=None): -+ """ -+ Check that the value is a list of values. -+ -+ You can optionally specify the minimum and maximum number of members. -+ -+ It does no check on list members. -+ -+ >>> vtor.check('list', ()) -+ [] -+ >>> vtor.check('list', []) -+ [] -+ >>> vtor.check('list', (1, 2)) -+ [1, 2] -+ >>> vtor.check('list', [1, 2]) -+ [1, 2] -+ >>> vtor.check('list(3)', (1, 2)) -+ Traceback (most recent call last): -+ VdtValueTooShortError: the value "(1, 2)" is too short. -+ >>> vtor.check('list(max=5)', (1, 2, 3, 4, 5, 6)) -+ Traceback (most recent call last): -+ VdtValueTooLongError: the value "(1, 2, 3, 4, 5, 6)" is too long. -+ >>> vtor.check('list(min=3, max=5)', (1, 2, 3, 4)) -+ [1, 2, 3, 4] -+ >>> vtor.check('list', 0) -+ Traceback (most recent call last): -+ VdtTypeError: the value "0" is of the wrong type. -+ >>> vtor.check('list', '12') -+ Traceback (most recent call last): -+ VdtTypeError: the value "12" is of the wrong type. -+ """ -+ (min_len, max_len) = _is_num_param(('min', 'max'), (min, max)) -+ if isinstance(value, StringTypes): -+ raise VdtTypeError(value) -+ try: -+ num_members = len(value) -+ except TypeError: -+ raise VdtTypeError(value) -+ if min_len is not None and num_members < min_len: -+ raise VdtValueTooShortError(value) -+ if max_len is not None and num_members > max_len: -+ raise VdtValueTooLongError(value) -+ return list(value) -+ -+ -+def is_tuple(value, min=None, max=None): -+ """ -+ Check that the value is a tuple of values. -+ -+ You can optionally specify the minimum and maximum number of members. -+ -+ It does no check on members. -+ -+ >>> vtor.check('tuple', ()) -+ () -+ >>> vtor.check('tuple', []) -+ () -+ >>> vtor.check('tuple', (1, 2)) -+ (1, 2) -+ >>> vtor.check('tuple', [1, 2]) -+ (1, 2) -+ >>> vtor.check('tuple(3)', (1, 2)) -+ Traceback (most recent call last): -+ VdtValueTooShortError: the value "(1, 2)" is too short. -+ >>> vtor.check('tuple(max=5)', (1, 2, 3, 4, 5, 6)) -+ Traceback (most recent call last): -+ VdtValueTooLongError: the value "(1, 2, 3, 4, 5, 6)" is too long. -+ >>> vtor.check('tuple(min=3, max=5)', (1, 2, 3, 4)) -+ (1, 2, 3, 4) -+ >>> vtor.check('tuple', 0) -+ Traceback (most recent call last): -+ VdtTypeError: the value "0" is of the wrong type. -+ >>> vtor.check('tuple', '12') -+ Traceback (most recent call last): -+ VdtTypeError: the value "12" is of the wrong type. -+ """ -+ return tuple(is_list(value, min, max)) -+ -+ -+def is_string(value, min=None, max=None): -+ """ -+ Check that the supplied value is a string. -+ -+ You can optionally specify the minimum and maximum number of members. -+ -+ >>> vtor.check('string', '0') -+ '0' -+ >>> vtor.check('string', 0) -+ Traceback (most recent call last): -+ VdtTypeError: the value "0" is of the wrong type. -+ >>> vtor.check('string(2)', '12') -+ '12' -+ >>> vtor.check('string(2)', '1') -+ Traceback (most recent call last): -+ VdtValueTooShortError: the value "1" is too short. -+ >>> vtor.check('string(min=2, max=3)', '123') -+ '123' -+ >>> vtor.check('string(min=2, max=3)', '1234') -+ Traceback (most recent call last): -+ VdtValueTooLongError: the value "1234" is too long. -+ """ -+ if not isinstance(value, StringTypes): -+ raise VdtTypeError(value) -+ (min_len, max_len) = _is_num_param(('min', 'max'), (min, max)) -+ try: -+ num_members = len(value) -+ except TypeError: -+ raise VdtTypeError(value) -+ if min_len is not None and num_members < min_len: -+ raise VdtValueTooShortError(value) -+ if max_len is not None and num_members > max_len: -+ raise VdtValueTooLongError(value) -+ return value -+ -+ -+def is_int_list(value, min=None, max=None): -+ """ -+ Check that the value is a list of integers. -+ -+ You can optionally specify the minimum and maximum number of members. -+ -+ Each list member is checked that it is an integer. -+ -+ >>> vtor.check('int_list', ()) -+ [] -+ >>> vtor.check('int_list', []) -+ [] -+ >>> vtor.check('int_list', (1, 2)) -+ [1, 2] -+ >>> vtor.check('int_list', [1, 2]) -+ [1, 2] -+ >>> vtor.check('int_list', [1, 'a']) -+ Traceback (most recent call last): -+ VdtTypeError: the value "a" is of the wrong type. -+ """ -+ return [is_integer(mem) for mem in is_list(value, min, max)] -+ -+ -+def is_bool_list(value, min=None, max=None): -+ """ -+ Check that the value is a list of booleans. -+ -+ You can optionally specify the minimum and maximum number of members. -+ -+ Each list member is checked that it is a boolean. -+ -+ >>> vtor.check('bool_list', ()) -+ [] -+ >>> vtor.check('bool_list', []) -+ [] -+ >>> check_res = vtor.check('bool_list', (True, False)) -+ >>> check_res == [True, False] -+ 1 -+ >>> check_res = vtor.check('bool_list', [True, False]) -+ >>> check_res == [True, False] -+ 1 -+ >>> vtor.check('bool_list', [True, 'a']) -+ Traceback (most recent call last): -+ VdtTypeError: the value "a" is of the wrong type. -+ """ -+ return [is_boolean(mem) for mem in is_list(value, min, max)] -+ -+ -+def is_float_list(value, min=None, max=None): -+ """ -+ Check that the value is a list of floats. -+ -+ You can optionally specify the minimum and maximum number of members. -+ -+ Each list member is checked that it is a float. -+ -+ >>> vtor.check('float_list', ()) -+ [] -+ >>> vtor.check('float_list', []) -+ [] -+ >>> vtor.check('float_list', (1, 2.0)) -+ [1.0, 2.0] -+ >>> vtor.check('float_list', [1, 2.0]) -+ [1.0, 2.0] -+ >>> vtor.check('float_list', [1, 'a']) -+ Traceback (most recent call last): -+ VdtTypeError: the value "a" is of the wrong type. -+ """ -+ return [is_float(mem) for mem in is_list(value, min, max)] -+ -+ -+def is_string_list(value, min=None, max=None): -+ """ -+ Check that the value is a list of strings. -+ -+ You can optionally specify the minimum and maximum number of members. -+ -+ Each list member is checked that it is a string. -+ -+ >>> vtor.check('string_list', ()) -+ [] -+ >>> vtor.check('string_list', []) -+ [] -+ >>> vtor.check('string_list', ('a', 'b')) -+ ['a', 'b'] -+ >>> vtor.check('string_list', ['a', 1]) -+ Traceback (most recent call last): -+ VdtTypeError: the value "1" is of the wrong type. -+ >>> vtor.check('string_list', 'hello') -+ Traceback (most recent call last): -+ VdtTypeError: the value "hello" is of the wrong type. -+ """ -+ if isinstance(value, StringTypes): -+ raise VdtTypeError(value) -+ return [is_string(mem) for mem in is_list(value, min, max)] -+ -+ -+def is_ip_addr_list(value, min=None, max=None): -+ """ -+ Check that the value is a list of IP addresses. -+ -+ You can optionally specify the minimum and maximum number of members. -+ -+ Each list member is checked that it is an IP address. -+ -+ >>> vtor.check('ip_addr_list', ()) -+ [] -+ >>> vtor.check('ip_addr_list', []) -+ [] -+ >>> vtor.check('ip_addr_list', ('1.2.3.4', '5.6.7.8')) -+ ['1.2.3.4', '5.6.7.8'] -+ >>> vtor.check('ip_addr_list', ['a']) -+ Traceback (most recent call last): -+ VdtValueError: the value "a" is unacceptable. -+ """ -+ return [is_ip_addr(mem) for mem in is_list(value, min, max)] -+ -+ -+fun_dict = { -+ 'integer': is_integer, -+ 'float': is_float, -+ 'ip_addr': is_ip_addr, -+ 'string': is_string, -+ 'boolean': is_boolean, -+} -+ -+ -+def is_mixed_list(value, *args): -+ """ -+ Check that the value is a list. -+ Allow specifying the type of each member. -+ Work on lists of specific lengths. -+ -+ You specify each member as a positional argument specifying type -+ -+ Each type should be one of the following strings : -+ 'integer', 'float', 'ip_addr', 'string', 'boolean' -+ -+ So you can specify a list of two strings, followed by -+ two integers as : -+ -+ mixed_list('string', 'string', 'integer', 'integer') -+ -+ The length of the list must match the number of positional -+ arguments you supply. -+ -+ >>> mix_str = "mixed_list('integer', 'float', 'ip_addr', 'string', 'boolean')" -+ >>> check_res = vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', True)) -+ >>> check_res == [1, 2.0, '1.2.3.4', 'a', True] -+ 1 -+ >>> check_res = vtor.check(mix_str, ('1', '2.0', '1.2.3.4', 'a', 'True')) -+ >>> check_res == [1, 2.0, '1.2.3.4', 'a', True] -+ 1 -+ >>> vtor.check(mix_str, ('b', 2.0, '1.2.3.4', 'a', True)) -+ Traceback (most recent call last): -+ VdtTypeError: the value "b" is of the wrong type. -+ >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a')) -+ Traceback (most recent call last): -+ VdtValueTooShortError: the value "(1, 2.0, '1.2.3.4', 'a')" is too short. -+ >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', 1, 'b')) -+ Traceback (most recent call last): -+ VdtValueTooLongError: the value "(1, 2.0, '1.2.3.4', 'a', 1, 'b')" is too long. -+ >>> vtor.check(mix_str, 0) -+ Traceback (most recent call last): -+ VdtTypeError: the value "0" is of the wrong type. -+ -+ This test requires an elaborate setup, because of a change in error string -+ output from the interpreter between Python 2.2 and 2.3 . -+ -+ >>> res_seq = ( -+ ... 'passed an incorrect value "', -+ ... 'yoda', -+ ... '" for parameter "mixed_list".', -+ ... ) -+ >>> if INTP_VER == (2, 2): -+ ... res_str = "".join(res_seq) -+ ... else: -+ ... res_str = "'".join(res_seq) -+ >>> try: -+ ... vtor.check('mixed_list("yoda")', ('a')) -+ ... except VdtParamError, err: -+ ... str(err) == res_str -+ 1 -+ """ -+ try: -+ length = len(value) -+ except TypeError: -+ raise VdtTypeError(value) -+ if length < len(args): -+ raise VdtValueTooShortError(value) -+ elif length > len(args): -+ raise VdtValueTooLongError(value) -+ try: -+ return [fun_dict[arg](val) for arg, val in zip(args, value)] -+ except KeyError, e: -+ raise VdtParamError('mixed_list', e) -+ -+ -+def is_option(value, *options): -+ """ -+ This check matches the value to any of a set of options. -+ -+ >>> vtor.check('option("yoda", "jedi")', 'yoda') -+ 'yoda' -+ >>> vtor.check('option("yoda", "jedi")', 'jed') -+ Traceback (most recent call last): -+ VdtValueError: the value "jed" is unacceptable. -+ >>> vtor.check('option("yoda", "jedi")', 0) -+ Traceback (most recent call last): -+ VdtTypeError: the value "0" is of the wrong type. -+ """ -+ if not isinstance(value, StringTypes): -+ raise VdtTypeError(value) -+ if not value in options: -+ raise VdtValueError(value) -+ return value -+ -+ -+def _test(value, *args, **keywargs): -+ """ -+ A function that exists for test purposes. -+ -+ >>> checks = [ -+ ... '3, 6, min=1, max=3, test=list(a, b, c)', -+ ... '3', -+ ... '3, 6', -+ ... '3,', -+ ... 'min=1, test="a b c"', -+ ... 'min=5, test="a, b, c"', -+ ... 'min=1, max=3, test="a, b, c"', -+ ... 'min=-100, test=-99', -+ ... 'min=1, max=3', -+ ... '3, 6, test="36"', -+ ... '3, 6, test="a, b, c"', -+ ... '3, max=3, test=list("a", "b", "c")', -+ ... '''3, max=3, test=list("'a'", 'b', "x=(c)")''', -+ ... "test='x=fish(3)'", -+ ... ] -+ >>> v = Validator({'test': _test}) -+ >>> for entry in checks: -+ ... print v.check(('test(%s)' % entry), 3) -+ (3, ('3', '6'), {'test': ['a', 'b', 'c'], 'max': '3', 'min': '1'}) -+ (3, ('3',), {}) -+ (3, ('3', '6'), {}) -+ (3, ('3',), {}) -+ (3, (), {'test': 'a b c', 'min': '1'}) -+ (3, (), {'test': 'a, b, c', 'min': '5'}) -+ (3, (), {'test': 'a, b, c', 'max': '3', 'min': '1'}) -+ (3, (), {'test': '-99', 'min': '-100'}) -+ (3, (), {'max': '3', 'min': '1'}) -+ (3, ('3', '6'), {'test': '36'}) -+ (3, ('3', '6'), {'test': 'a, b, c'}) -+ (3, ('3',), {'test': ['a', 'b', 'c'], 'max': '3'}) -+ (3, ('3',), {'test': ["'a'", 'b', 'x=(c)'], 'max': '3'}) -+ (3, (), {'test': 'x=fish(3)'}) -+ -+ >>> v = Validator() -+ >>> v.check('integer(default=6)', '3') -+ 3 -+ >>> v.check('integer(default=6)', None, True) -+ 6 -+ >>> v.get_default_value('integer(default=6)') -+ 6 -+ >>> v.get_default_value('float(default=6)') -+ 6.0 -+ >>> v.get_default_value('pass(default=None)') -+ >>> v.get_default_value("string(default='None')") -+ 'None' -+ >>> v.get_default_value('pass') -+ Traceback (most recent call last): -+ KeyError: 'Check "pass" has no default value.' -+ >>> v.get_default_value('pass(default=list(1, 2, 3, 4))') -+ ['1', '2', '3', '4'] -+ -+ >>> v = Validator() -+ >>> v.check("pass(default=None)", None, True) -+ >>> v.check("pass(default='None')", None, True) -+ 'None' -+ >>> v.check('pass(default="None")', None, True) -+ 'None' -+ >>> v.check('pass(default=list(1, 2, 3, 4))', None, True) -+ ['1', '2', '3', '4'] -+ -+ Bug test for unicode arguments -+ >>> v = Validator() -+ >>> v.check(u'string(min=4)', u'test') -+ u'test' -+ -+ >>> v = Validator() -+ >>> v.get_default_value(u'string(min=4, default="1234")') -+ u'1234' -+ >>> v.check(u'string(min=4, default="1234")', u'test') -+ u'test' -+ -+ >>> v = Validator() -+ >>> default = v.get_default_value('string(default=None)') -+ >>> default == None -+ 1 -+ """ -+ return (value, args, keywargs) -+ -+ -+if __name__ == '__main__': -+ # run the code tests in doctest format -+ import doctest -+ m = sys.modules.get('__main__') -+ globs = m.__dict__.copy() -+ globs.update({ -+ 'INTP_VER': INTP_VER, -+ 'vtor': Validator(), -+ }) -+ doctest.testmod(m, globs=globs) Index: ipython-0.10/setupbase.py =================================================================== --- ipython-0.10.orig/setupbase.py diff --git a/ipython.spec b/ipython.spec index c9cad24..fede0c1 100644 --- a/ipython.spec +++ b/ipython.spec @@ -3,8 +3,8 @@ %endif Name: ipython -Version: 0.10 -Release: 8%{?dist} +Version: 0.10.1 +Release: 1%{?dist} Summary: An enhanced interactive Python shell Group: Development/Libraries @@ -89,7 +89,29 @@ This package contains the gui of %{name}, which requires wxPython. %prep %setup -q + %patch0 -p1 +# help with unbundling (don't use diffs to move files around) +pushd IPython/external +mkdir argparse +mv argparse.py argparse/_argparse.py +mkdir configobj +mv configobj.py configobj/_configobj.py +mkdir guid +mv guid.py guid/_guid.py +mkdir Itpl +mv Itpl.py Itpl/_Itpl.py +mkdir mglob +mv mglob.py mglob/_mglob.py +mkdir path +mv path.py path/_path.py +mkdir pretty +mv pretty.py pretty/_pretty.py +mkdir simplegeneric +mv simplegeneric.py simplegeneric/_simplegeneric.py +mkdir validate +mv validate.py validate/_validate.py +popd %patch1 -p1 %patch2 -p1 @@ -207,6 +229,10 @@ rm -rf %{buildroot} %changelog +* Wed Oct 13 2010 Thomas Spura - 0.10.1-1 +- unbundle a bit differently +- update to new version + * Tue Aug 31 2010 Thomas Spura - 0.10-8 - pycolor: wrong filename -> no crash (#628742) diff --git a/sources b/sources index 90ba8b5..b44f71b 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -dd10cd1b622c16c1afca2239fcc0dfdf ipython-0.10.tar.gz +54ae47079b0e9a0998593a99ce76ec1f ipython-0.10.1.tar.gz