ipython/ipython-unbundle-external-module.patch
2010-06-21 18:39:44 +00:00

17449 lines
612 KiB
Diff

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 <steven.bethard@gmail.com>.
+#
+# 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
+++ ipython-0.10/IPython/external/argparse/__init__.py
@@ -0,0 +1,11 @@
+try:
+ import argparse
+ # Workaround an argparse bug
+ if 'RawTextHelpFormatterArgumentDefaultsHelpFormatter' in argparse.__all__:
+ import itertools
+ argparse.__all__ = list(itertools.chain( [i for i in argparse.__all__
+ if i != 'RawTextHelpFormatterArgumentDefaultsHelpFormatter'],
+ ['RawTextHelpFormatter', 'ArgumentDefaultsHelpFormatter']))
+ 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 <steven.bethard@gmail.com>.
-#
-# 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<escaped>\$) | # Two $ signs
+ (?P<named>[_a-z][_a-z0-9]*) | # $name format
+ {(?P<braced>[^}]*)} # ${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
+ (?:(?<!,)) # Empty value
+ )? # last item in a list - or string value
+ )|
+ (,) # alternatively a single comma - empty list
+ )
+ \s*(\#.*)? # optional comment
+ $''',
+ re.VERBOSE)
+
+ # use findall to get the members of a list value
+ _listvalueexp = re.compile(r'''
+ (
+ (?:".*?")| # double quotes
+ (?:'.*?')| # single quotes
+ (?:[^'",\#].*?) # unquoted
+ )
+ \s*,\s* # comma
+ ''',
+ re.VERBOSE)
+
+ # this regexp is used for the value
+ # when lists are switched off
+ _nolistvalue = re.compile(r'''^
+ (
+ (?:".*?")| # double quotes
+ (?:'.*?')| # single quotes
+ (?:[^'"\#].*?)| # unquoted
+ (?:) # Empty value
+ )
+ \s*(\#.*)? # optional comment
+ $''',
+ re.VERBOSE)
+
+ # regexes for finding triple quoted values on one line
+ _single_line_single = re.compile(r"^'''(.*?)'''\s*(#.*)?$")
+ _single_line_double = re.compile(r'^"""(.*?)"""\s*(#.*)?$')
+ _multi_line_single = re.compile(r"^(.*?)'''\s*(#.*)?$")
+ _multi_line_double = re.compile(r'^(.*?)"""\s*(#.*)?$')
+
+ _triple_quote = {
+ "'''": (_single_line_single, _multi_line_single),
+ '"""': (_single_line_double, _multi_line_double),
+ }
+
+ # Used by the ``istrue`` Section method
+ _bools = {
+ 'yes': True, 'no': False,
+ 'on': True, 'off': False,
+ '1': True, '0': False,
+ 'true': True, 'false': False,
+ }
+
+
+ def __init__(self, infile=None, options=None, **kwargs):
+ """
+ Parse a config file or create a config file object.
+
+ ``ConfigObj(infile=None, options=None, **kwargs)``
+ """
+ # init the superclass
+ Section.__init__(self, self, 0, self)
+
+ if infile is None:
+ infile = []
+ if options is None:
+ options = {}
+ else:
+ options = dict(options)
+
+ # keyword arguments take precedence over an options dictionary
+ options.update(kwargs)
+
+ defaults = OPTION_DEFAULTS.copy()
+ # TODO: check the values too.
+ for entry in options:
+ if entry not in defaults:
+ raise TypeError('Unrecognised option "%s".' % entry)
+
+ # Add any explicit options to the defaults
+ defaults.update(options)
+ self._initialise(defaults)
+ configspec = defaults['configspec']
+ self._original_configspec = configspec
+ self._load(infile, configspec)
+
+
+ def _load(self, infile, configspec):
+ if isinstance(infile, StringTypes):
+ self.filename = infile
+ if os.path.isfile(infile):
+ h = open(infile, 'rb')
+ infile = h.read() or []
+ h.close()
+ elif self.file_error:
+ # raise an error if the file doesn't exist
+ raise IOError('Config file not found: "%s".' % self.filename)
+ else:
+ # file doesn't already exist
+ if self.create_empty:
+ # this is a good test that the filename specified
+ # isn't impossible - like on a non-existent device
+ h = open(infile, 'w')
+ h.write('')
+ h.close()
+ infile = []
+
+ elif isinstance(infile, (list, tuple)):
+ infile = list(infile)
+
+ elif isinstance(infile, dict):
+ # initialise self
+ # the Section class handles creating subsections
+ if isinstance(infile, ConfigObj):
+ # get a copy of our ConfigObj
+ infile = infile.dict()
+
+ for entry in infile:
+ self[entry] = infile[entry]
+ del self._errors
+
+ if configspec is not None:
+ self._handle_configspec(configspec)
+ else:
+ self.configspec = None
+ return
+
+ elif hasattr(infile, 'read'):
+ # This supports file like objects
+ infile = infile.read() or []
+ # needs splitting into lines - but needs doing *after* decoding
+ # in case it's not an 8 bit encoding
+ else:
+ raise TypeError('infile must be a filename, file like object, or list of lines.')
+
+ if infile:
+ # don't do it for the empty ConfigObj
+ infile = self._handle_bom(infile)
+ # infile is now *always* a list
+ #
+ # Set the newlines attribute (first line ending it finds)
+ # and strip trailing '\n' or '\r' from lines
+ for line in infile:
+ if (not line) or (line[-1] not in ('\r', '\n', '\r\n')):
+ continue
+ for end in ('\r\n', '\n', '\r'):
+ if line.endswith(end):
+ self.newlines = end
+ break
+ break
+
+ infile = [line.rstrip('\r\n') for line in infile]
+
+ self._parse(infile)
+ # if we had any errors, now is the time to raise them
+ if self._errors:
+ info = "at line %s." % self._errors[0].line_number
+ if len(self._errors) > 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
+++ ipython-0.10/IPython/external/configobj/__init__.py
@@ -0,0 +1,4 @@
+try:
+ 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<escaped>\$) | # Two $ signs
- (?P<named>[_a-z][_a-z0-9]*) | # $name format
- {(?P<braced>[^}]*)} # ${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
- (?:(?<!,)) # Empty value
- )? # last item in a list - or string value
- )|
- (,) # alternatively a single comma - empty list
- )
- \s*(\#.*)? # optional comment
- $''',
- re.VERBOSE)
-
- # use findall to get the members of a list value
- _listvalueexp = re.compile(r'''
- (
- (?:".*?")| # double quotes
- (?:'.*?')| # single quotes
- (?:[^'",\#].*?) # unquoted
- )
- \s*,\s* # comma
- ''',
- re.VERBOSE)
-
- # this regexp is used for the value
- # when lists are switched off
- _nolistvalue = re.compile(r'''^
- (
- (?:".*?")| # double quotes
- (?:'.*?')| # single quotes
- (?:[^'"\#].*?)| # unquoted
- (?:) # Empty value
- )
- \s*(\#.*)? # optional comment
- $''',
- re.VERBOSE)
-
- # regexes for finding triple quoted values on one line
- _single_line_single = re.compile(r"^'''(.*?)'''\s*(#.*)?$")
- _single_line_double = re.compile(r'^"""(.*?)"""\s*(#.*)?$')
- _multi_line_single = re.compile(r"^(.*?)'''\s*(#.*)?$")
- _multi_line_double = re.compile(r'^(.*?)"""\s*(#.*)?$')
-
- _triple_quote = {
- "'''": (_single_line_single, _multi_line_single),
- '"""': (_single_line_double, _multi_line_double),
- }
-
- # Used by the ``istrue`` Section method
- _bools = {
- 'yes': True, 'no': False,
- 'on': True, 'off': False,
- '1': True, '0': False,
- 'true': True, 'false': False,
- }
-
-
- def __init__(self, infile=None, options=None, **kwargs):
- """
- Parse a config file or create a config file object.
-
- ``ConfigObj(infile=None, options=None, **kwargs)``
- """
- # init the superclass
- Section.__init__(self, self, 0, self)
-
- if infile is None:
- infile = []
- if options is None:
- options = {}
- else:
- options = dict(options)
-
- # keyword arguments take precedence over an options dictionary
- options.update(kwargs)
-
- defaults = OPTION_DEFAULTS.copy()
- # TODO: check the values too.
- for entry in options:
- if entry not in defaults:
- raise TypeError('Unrecognised option "%s".' % entry)
-
- # Add any explicit options to the defaults
- defaults.update(options)
- self._initialise(defaults)
- configspec = defaults['configspec']
- self._original_configspec = configspec
- self._load(infile, configspec)
-
-
- def _load(self, infile, configspec):
- if isinstance(infile, StringTypes):
- self.filename = infile
- if os.path.isfile(infile):
- h = open(infile, 'rb')
- infile = h.read() or []
- h.close()
- elif self.file_error:
- # raise an error if the file doesn't exist
- raise IOError('Config file not found: "%s".' % self.filename)
- else:
- # file doesn't already exist
- if self.create_empty:
- # this is a good test that the filename specified
- # isn't impossible - like on a non-existent device
- h = open(infile, 'w')
- h.write('')
- h.close()
- infile = []
-
- elif isinstance(infile, (list, tuple)):
- infile = list(infile)
-
- elif isinstance(infile, dict):
- # initialise self
- # the Section class handles creating subsections
- if isinstance(infile, ConfigObj):
- # get a copy of our ConfigObj
- infile = infile.dict()
-
- for entry in infile:
- self[entry] = infile[entry]
- del self._errors
-
- if configspec is not None:
- self._handle_configspec(configspec)
- else:
- self.configspec = None
- return
-
- elif hasattr(infile, 'read'):
- # This supports file like objects
- infile = infile.read() or []
- # needs splitting into lines - but needs doing *after* decoding
- # in case it's not an 8 bit encoding
- else:
- raise TypeError('infile must be a filename, file like object, or list of lines.')
-
- if infile:
- # don't do it for the empty ConfigObj
- infile = self._handle_bom(infile)
- # infile is now *always* a list
- #
- # Set the newlines attribute (first line ending it finds)
- # and strip trailing '\n' or '\r' from lines
- for line in infile:
- if (not line) or (line[-1] not in ('\r', '\n', '\r\n')):
- continue
- for end in ('\r\n', '\n', '\r'):
- if line.endswith(end):
- self.newlines = end
- break
- break
-
- infile = [line.rstrip('\r\n') for line in infile]
-
- self._parse(infile)
- # if we had any errors, now is the time to raise them
- if self._errors:
- info = "at line %s." % self._errors[0].line_number
- if len(self._errors) > 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
+++ ipython-0.10/IPython/external/guid/__init__.py
@@ -0,0 +1,4 @@
+try:
+ 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
+++ ipython-0.10/IPython/external/Itpl/__init__.py
@@ -0,0 +1,4 @@
+try:
+ 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 <ping@lfw.org>
+#
+#
+# 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 <ping@lfw.org>'
+__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 "<Itpl %s >" % 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 "<ItplNS %s >" % 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 "<interpolated " + repr(self.file) + ">"
+ 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 <ping@lfw.org>
-#
-#
-# 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 <ping@lfw.org>'
-__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 "<Itpl %s >" % 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 "<ItplNS %s >" % 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 "<interpolated " + repr(self.file) + ">"
- 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
+++ ipython-0.10/IPython/external/mglob/__init__.py
@@ -0,0 +1,4 @@
+try:
+ 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 <vivainio@gmail.com>
+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 <vivainio@gmail.com>
-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
+++ ipython-0.10/IPython/external/path/__init__.py
@@ -0,0 +1,4 @@
+try:
+ 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 <jason.orendorff\x40gmail\x2ecom> (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, <the rest of p>).
+
+ 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 <jason.orendorff\x40gmail\x2ecom> (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, <the rest of p>).
-
- 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
+++ ipython-0.10/IPython/external/pretty/__init__.py
@@ -0,0 +1,4 @@
+try:
+ 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, '<super: ')
+ p.pretty(obj.__self_class__)
+ p.text(',')
+ p.breakable()
+ p.pretty(obj.__self__)
+ p.end_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('<function %s>' % 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('<dictproxy {', '}>'),
+ 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, '<super: ')
- p.pretty(obj.__self_class__)
- p.text(',')
- p.breakable()
- p.pretty(obj.__self__)
- p.end_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('<function %s>' % 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('<dictproxy {', '}>'),
- 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
+++ ipython-0.10/IPython/external/simplegeneric/__init__.py
@@ -0,0 +1,4 @@
+try:
+ 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
+++ ipython-0.10/IPython/external/validate/__init__.py
@@ -0,0 +1,8 @@
+try:
+ import validate
+ if '__docformat__' in validate.__all__ and validate.__version__.split('.') >= ['1', '0', '1']:
+ # __docformat__ was removed in 1.0.1 but
+ validate.__all__ = [i for i in validate.__all__ if i != '__docformat__']
+ 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
+++ ipython-0.10/setupbase.py
@@ -106,6 +106,15 @@ def find_packages():
add_package(packages, 'config', tests=True)
add_package(packages , 'Extensions')
add_package(packages, 'external')
+ add_package(packages, 'external.argparse')
+ add_package(packages, 'external.configobj')
+ add_package(packages, 'external.guid')
+ add_package(packages, 'external.Itpl')
+ add_package(packages, 'external.mglob')
+ add_package(packages, 'external.path')
+ add_package(packages, 'external.pretty')
+ add_package(packages, 'external.simplegeneric')
+ add_package(packages, 'external.validate')
add_package(packages, 'gui')
add_package(packages, 'gui.wx')
add_package(packages, 'frontend', tests=True)