[Zope-Checkins] CVS: Zope/lib/python/docutils - __init__.py:1.3 core.py:1.3 frontend.py:1.3 io.py:1.3 nodes.py:1.3 statemachine.py:1.3 utils.py:1.3

Chris McDonough chrism@zope.com
13 Jul 2003 21:06:06 -0400


The optparse module needed by frontend.py isn't included in Python
2.2.3, so zope doesn't start with this latest round of checkins unless
you're running Python 2.3. 

I'm not sure what to do.  I don't want to revert these checkins.  The
optik module that ships along with docutils appears to be an earlier
version of optparse.  Should we just change docutils to import from that
or should we include the 2.3 optparse, assuming it works under 2.2? 

- C 


On Thu, 2003-07-10 at 11:50, Andreas Jung wrote: 
> Update of /cvs-repository/Zope/lib/python/docutils
> In directory cvs.zope.org:/tmp/cvs-serv8556
> 
> Modified Files:
> 	__init__.py core.py frontend.py io.py nodes.py statemachine.py 
> 	utils.py 
> Log Message:
> docutils update
> 
> 
> === Zope/lib/python/docutils/__init__.py 1.2 => 1.3 ===
> --- Zope/lib/python/docutils/__init__.py:1.2	Sat Feb  1 04:26:00 2003
> +++ Zope/lib/python/docutils/__init__.py	Thu Jul 10 11:49:30 2003
> @@ -23,12 +23,6 @@
>  
>  - nodes.py: Docutils document tree (doctree) node class library.
>  
> -- optik.py: Option parsing and command-line help; from Greg Ward's
> -  http://optik.sf.net/ project, included for convenience.
> -
> -- roman.py: Conversion to and from Roman numerals. Courtesy of Mark
> -  Pilgrim (http://diveintopython.org/).
> -
>  - statemachine.py: A finite state machine specialized for
>    regular-expression-based text filters.
>  
> @@ -55,12 +49,12 @@
>  
>  __docformat__ = 'reStructuredText'
>  
> -__version__ = '0.2.8'
> +__version__ = '0.3.0'
>  """``major.minor.micro`` version number.  The micro number is bumped any time
>  there's a change in the API incompatible with one of the front ends.  The
>  minor number is bumped whenever there is a project release.  The major number
> -will be bumped when the project is complete, and perhaps if there is a major
> -change in the design."""
> +will be bumped when the project is feature-complete, and perhaps if there is a
> +major change in the design."""
>  
>  
>  class ApplicationError(StandardError): pass
> @@ -85,7 +79,11 @@
>      and/or description may be `None`; no group title implies no group, just a
>      list of single options.  Runtime settings names are derived implicitly
>      from long option names ("--a-setting" becomes ``settings.a_setting``) or
> -    explicitly from the "destination" keyword argument."""
> +    explicitly from the "dest" keyword argument."""
> +
> +    settings_defaults = None
> +    """A dictionary of defaults for internal or inaccessible (by command-line
> +    or config file) settings.  Override in subclasses."""
>  
>      settings_default_overrides = None
>      """A dictionary of auxiliary defaults, to override defaults for settings
> 
> 
> === Zope/lib/python/docutils/core.py 1.2 => 1.3 ===
> --- Zope/lib/python/docutils/core.py:1.2	Sat Feb  1 04:26:00 2003
> +++ Zope/lib/python/docutils/core.py	Thu Jul 10 11:49:30 2003
> @@ -15,9 +15,9 @@
>  __docformat__ = 'reStructuredText'
>  
>  import sys
> -from docutils import Component
> -from docutils import frontend, io, readers, parsers, writers
> -from docutils.frontend import OptionParser, ConfigParser
> +from docutils import Component, __version__
> +from docutils import frontend, io, utils, readers, parsers, writers
> +from docutils.frontend import OptionParser
>  
>  
>  class Publisher:
> @@ -87,14 +87,8 @@
>          #@@@ Add self.source & self.destination to components in future?
>          option_parser = OptionParser(
>              components=(settings_spec, self.parser, self.reader, self.writer),
> +            defaults=defaults, read_config_files=1,
>              usage=usage, description=description)
> -        config = ConfigParser()
> -        config.read_standard_files()
> -        config_settings = config.get_section('options')
> -        frontend.make_paths_absolute(config_settings,
> -                                     option_parser.relative_path_settings)
> -        defaults.update(config_settings)
> -        option_parser.set_defaults(**defaults)
>          return option_parser
>  
>      def get_settings(self, usage=None, description=None,
> @@ -148,7 +142,8 @@
>              self.settings._destination = destination_path
>          self.destination = self.destination_class(
>              destination=destination, destination_path=destination_path,
> -            encoding=self.settings.output_encoding)
> +            encoding=self.settings.output_encoding,
> +            error_handler=self.settings.output_encoding_error_handler)
>  
>      def apply_transforms(self, document):
>          document.transformer.populate_from_components(
> @@ -157,7 +152,8 @@
>          document.transformer.apply_transforms()
>  
>      def publish(self, argv=None, usage=None, description=None,
> -                settings_spec=None, settings_overrides=None):
> +                settings_spec=None, settings_overrides=None,
> +                enable_exit=None):
>          """
>          Process command line options and arguments (if `self.settings` not
>          already set), run `self.reader` and then `self.writer`.  Return
> @@ -169,25 +165,52 @@
>          elif settings_overrides:
>              self.settings._update(settings_overrides, 'loose')
>          self.set_io()
> -        document = self.reader.read(self.source, self.parser, self.settings)
> -        self.apply_transforms(document)
> -        output = self.writer.write(document, self.destination)
> +        exit = None
> +        document = None
> +        try:
> +            document = self.reader.read(self.source, self.parser,
> +                                        self.settings)
> +            self.apply_transforms(document)
> +            output = self.writer.write(document, self.destination)
> +        except utils.SystemMessage, error:
> +            if self.settings.traceback:
> +                raise
> +            print >>sys.stderr, ('Exiting due to level-%s (%s) system message.'
> +                                 % (error.level,
> +                                    utils.Reporter.levels[error.level]))
> +            exit = 1
> +        except Exception, error:
> +            if self.settings.traceback:
> +                raise
> +            print >>sys.stderr, error
> +            print >>sys.stderr, ("""\
> +Exiting due to error.  Use "--traceback" to diagnose.
> +Please report errors to <docutils-users@lists.sf.net>.
> +Include "--traceback" output, Docutils version (%s),
> +Python version (%s), your OS type & version, and the
> +command line used.""" % (__version__, sys.version.split()[0]))
> +            exit = 1
>          if self.settings.dump_settings:
>              from pprint import pformat
>              print >>sys.stderr, '\n::: Runtime settings:'
>              print >>sys.stderr, pformat(self.settings.__dict__)
> -        if self.settings.dump_internals:
> +        if self.settings.dump_internals and document:
>              from pprint import pformat
>              print >>sys.stderr, '\n::: Document internals:'
>              print >>sys.stderr, pformat(document.__dict__)
> -        if self.settings.dump_transforms:
> +        if self.settings.dump_transforms and document:
>              from pprint import pformat
>              print >>sys.stderr, '\n::: Transforms applied:'
>              print >>sys.stderr, pformat(document.transformer.applied)
> -        if self.settings.dump_pseudo_xml:
> +        if self.settings.dump_pseudo_xml and document:
>              print >>sys.stderr, '\n::: Pseudo-XML:'
>              print >>sys.stderr, document.pformat().encode(
>                  'raw_unicode_escape')
> +        if enable_exit and document and (document.reporter.max_level
> +                                         >= self.settings.exit_level):
> +            sys.exit(document.reporter.max_level + 10)
> +        elif exit:
> +            sys.exit(1)
>          return output
>  
>  
> @@ -199,7 +222,7 @@
>                      parser=None, parser_name='restructuredtext',
>                      writer=None, writer_name='pseudoxml',
>                      settings=None, settings_spec=None,
> -                    settings_overrides=None, argv=None,
> +                    settings_overrides=None, enable_exit=1, argv=None,
>                      usage=default_usage, description=default_description):
>      """
>      Set up & run a `Publisher`.  For command-line front ends.
> @@ -220,6 +243,7 @@
>        subclass.  Used only if no `settings` specified.
>      - `settings_overrides`: A dictionary containing program-specific overrides
>        of component settings.
> +    - `enable_exit`: Boolean; enable exit status at end of processing?
>      - `argv`: Command-line argument list to use instead of ``sys.argv[1:]``.
>      - `usage`: Usage string, output if there's a problem parsing the command
>        line.
> @@ -228,14 +252,16 @@
>      """
>      pub = Publisher(reader, parser, writer, settings=settings)
>      pub.set_components(reader_name, parser_name, writer_name)
> -    pub.publish(argv, usage, description, settings_spec, settings_overrides)
> +    pub.publish(argv, usage, description, settings_spec, settings_overrides,
> +                enable_exit=enable_exit)
>  
>  def publish_file(source=None, source_path=None,
>                   destination=None, destination_path=None,
>                   reader=None, reader_name='standalone',
>                   parser=None, parser_name='restructuredtext',
>                   writer=None, writer_name='pseudoxml',
> -                 settings=None, settings_spec=None, settings_overrides=None):
> +                 settings=None, settings_spec=None, settings_overrides=None,
> +                 enable_exit=None):
>      """
>      Set up & run a `Publisher`.  For programmatic use with file-like I/O.
>  
> @@ -263,6 +289,7 @@
>        subclass.  Used only if no `settings` specified.
>      - `settings_overrides`: A dictionary containing program-specific overrides
>        of component settings.
> +    - `enable_exit`: Boolean; enable exit status at end of processing?
>      """
>      pub = Publisher(reader, parser, writer, settings=settings)
>      pub.set_components(reader_name, parser_name, writer_name)
> @@ -272,21 +299,27 @@
>          settings._update(settings_overrides, 'loose')
>      pub.set_source(source, source_path)
>      pub.set_destination(destination, destination_path)
> -    pub.publish()
> +    pub.publish(enable_exit=enable_exit)
>  
>  def publish_string(source, source_path=None, destination_path=None, 
>                     reader=None, reader_name='standalone',
>                     parser=None, parser_name='restructuredtext',
>                     writer=None, writer_name='pseudoxml',
>                     settings=None, settings_spec=None,
> -                   settings_overrides=None):
> +                   settings_overrides=None, enable_exit=None):
>      """
>      Set up & run a `Publisher`, and return the string output.
>      For programmatic use with string I/O.
>  
>      For encoded string output, be sure to set the "output_encoding" setting to
>      the desired encoding.  Set it to "unicode" for unencoded Unicode string
> -    output.
> +    output.  Here's how::
> +
> +        publish_string(..., settings_overrides={'output_encoding': 'unicode'})
> +
> +    Similarly for Unicode string input (`source`)::
> +
> +        publish_string(..., settings_overrides={'input_encoding': 'unicode'})
>  
>      Parameters:
>  
> @@ -312,6 +345,7 @@
>        subclass.  Used only if no `settings` specified.
>      - `settings_overrides`: A dictionary containing program-specific overrides
>        of component settings.
> +    - `enable_exit`: Boolean; enable exit status at end of processing?
>      """
>      pub = Publisher(reader, parser, writer, settings=settings,
>                      source_class=io.StringInput,
> @@ -323,4 +357,4 @@
>          settings._update(settings_overrides, 'loose')
>      pub.set_source(source, source_path)
>      pub.set_destination(destination_path=destination_path)
> -    return pub.publish()
> +    return pub.publish(enable_exit=enable_exit)
> 
> 
> === Zope/lib/python/docutils/frontend.py 1.2 => 1.3 ===
> --- Zope/lib/python/docutils/frontend.py:1.2	Sat Feb  1 04:26:00 2003
> +++ Zope/lib/python/docutils/frontend.py	Thu Jul 10 11:49:30 2003
> @@ -19,10 +19,13 @@
>  
>  import os
>  import os.path
> +import sys
> +import types
>  import ConfigParser as CP
> +import codecs
>  import docutils
> -from docutils import optik
> -from docutils.optik import Values
> +import optparse
> +from optparse import Values, SUPPRESS_HELP
>  
>  
>  def store_multiple(option, opt, value, parser, *args, **kwargs):
> @@ -42,12 +45,85 @@
>      Read a configuration file during option processing.  (Option callback.)
>      """
>      config_parser = ConfigParser()
> -    config_parser.read(value)
> +    config_parser.read(value, parser)
>      settings = config_parser.get_section('options')
>      make_paths_absolute(settings, parser.relative_path_settings,
>                          os.path.dirname(value))
>      parser.values.__dict__.update(settings)
>  
> +def set_encoding(option, opt, value, parser):
> +    """
> +    Validate & set the encoding specified.  (Option callback.)
> +    """
> +    try:
> +        value = validate_encoding(option.dest, value)
> +    except LookupError, error:
> +        raise (optparse.OptionValueError('option "%s": %s' % (opt, error)),
> +               None, sys.exc_info()[2])
> +    setattr(parser.values, option.dest, value)
> +
> +def validate_encoding(name, value):
> +    try:
> +        codecs.lookup(value)
> +    except LookupError:
> +        raise (LookupError('unknown encoding: "%s"' % value),
> +               None, sys.exc_info()[2])
> +    return value
> +
> +def set_encoding_error_handler(option, opt, value, parser):
> +    """
> +    Validate & set the encoding error handler specified.  (Option callback.)
> +    """
> +    try:
> +        value = validate_encoding_error_handler(option.dest, value)
> +    except LookupError, error:
> +        raise (optparse.OptionValueError('option "%s": %s' % (opt, error)),
> +               None, sys.exc_info()[2])
> +    setattr(parser.values, option.dest, value)
> +
> +def validate_encoding_error_handler(name, value):
> +    try:
> +        codecs.lookup_error(value)
> +    except AttributeError:              # prior to Python 2.3
> +        if value not in ('strict', 'ignore', 'replace'):
> +            raise (LookupError(
> +                'unknown encoding error handler: "%s" (choices: '
> +                '"strict", "ignore", or "replace")' % value),
> +                   None, sys.exc_info()[2])
> +    except LookupError:
> +        raise (LookupError(
> +            'unknown encoding error handler: "%s" (choices: '
> +            '"strict", "ignore", "replace", "backslashreplace", '
> +            '"xmlcharrefreplace", and possibly others; see documentation for '
> +            'the Python ``codecs`` module)' % value),
> +               None, sys.exc_info()[2])
> +    return value
> +
> +def set_encoding_and_error_handler(option, opt, value, parser):
> +    """
> +    Validate & set the encoding and error handler specified.  (Option callback.)
> +    """
> +    try:
> +        value = validate_encoding_and_error_handler(option.dest, value)
> +    except LookupError, error:
> +        raise (optparse.OptionValueError('option "%s": %s' % (opt, error)),
> +               None, sys.exc_info()[2])
> +    if ':' in value:
> +        encoding, handler = value.split(':')
> +        setattr(parser.values, option.dest + '_error_handler', handler)
> +    else:
> +        encoding = value
> +    setattr(parser.values, option.dest, encoding)
> +
> +def validate_encoding_and_error_handler(name, value):
> +    if ':' in value:
> +        encoding, handler = value.split(':')
> +        validate_encoding_error_handler(name + '_error_handler', handler)
> +    else:
> +        encoding = value
> +    validate_encoding(name, encoding)
> +    return value
> +
>  def make_paths_absolute(pathdict, keys, base_path=None):
>      """
>      Interpret filesystem path settings relative to the `base_path` given.
> @@ -63,7 +139,7 @@
>                  os.path.abspath(os.path.join(base_path, pathdict[key])))
>  
>  
> -class OptionParser(optik.OptionParser, docutils.SettingsSpec):
> +class OptionParser(optparse.OptionParser, docutils.SettingsSpec):
>  
>      """
>      Parser for command-line and library use.  The `settings_spec`
> @@ -81,6 +157,11 @@
>      thresholds = {'info': 1, 'warning': 2, 'error': 3, 'severe': 4, 'none': 5}
>      """Lookup table for --report and --halt threshold values."""
>  
> +    if hasattr(codecs, 'backslashreplace_errors'):
> +        default_error_encoding_error_handler = 'backslashreplace'
> +    else:
> +        default_error_encoding_error_handler = 'replace'
> +
>      settings_spec = (
>          'General Docutils Options',
>          None,
> @@ -147,17 +228,54 @@
>           ('Same as "--halt=info": halt processing at the slightest problem.',
>            ['--strict'], {'action': 'store_const', 'const': 'info',
>                           'dest': 'halt_level'}),
> -         ('Report debug-level system messages.',
> +         ('Enable a non-zero exit status for normal exit if non-halting '
> +          'system messages (at or above <level>) were generated.  Levels as '
> +          'in --report.  Default is 5 (disabled).  Exit status is the maximum '
> +          'system message level plus 10 (11 for INFO, etc.).',
> +          ['--exit'], {'choices': threshold_choices, 'dest': 'exit_level',
> +                       'default': 5, 'metavar': '<level>'}),
> +         ('Report debug-level system messages and generate diagnostic output.',
>            ['--debug'], {'action': 'store_true'}),
> -         ('Do not report debug-level system messages.',
> +         ('Do not report debug-level system messages or generate diagnostic '
> +          'output.',
>            ['--no-debug'], {'action': 'store_false', 'dest': 'debug'}),
>           ('Send the output of system messages (warnings) to <file>.',
>            ['--warnings'], {'dest': 'warning_stream', 'metavar': '<file>'}),
> +         ('Enable Python tracebacks when an error occurs.',
> +          ['--traceback'], {'action': 'store_true', 'default': None}),
> +         ('Disable Python tracebacks when errors occur; report just the error '
> +          'instead.  This is the default.',
> +          ['--no-traceback'], {'dest': 'traceback', 'action': 'store_false'}),
>           ('Specify the encoding of input text.  Default is locale-dependent.',
> -          ['--input-encoding', '-i'], {'metavar': '<name>'}),
> -         ('Specify the encoding for output.  Default is UTF-8.',
> +          ['--input-encoding', '-i'],
> +          {'action': 'callback', 'callback': set_encoding,
> +           'metavar': '<name>', 'type': 'string', 'dest': 'input_encoding'}),
> +         ('Specify the text encoding for output.  Default is UTF-8.  '
> +          'Optionally also specify the encoding error handler for unencodable '
> +          'characters (see "--error-encoding"); default is "strict".',
>            ['--output-encoding', '-o'],
> -          {'metavar': '<name>', 'default': 'utf-8'}),
> +          {'action': 'callback', 'callback': set_encoding_and_error_handler,
> +           'metavar': '<name[:handler]>', 'type': 'string',
> +           'dest': 'output_encoding', 'default': 'utf-8'}),
> +         (SUPPRESS_HELP,                # usually handled by --output-encoding
> +          ['--output_encoding_error_handler'],
> +          {'action': 'callback', 'callback': set_encoding_error_handler,
> +           'type': 'string', 'dest': 'output_encoding_error_handler',
> +           'default': 'strict'}),
> +         ('Specify the text encoding for error output.  Default is ASCII.  '
> +          'Optionally also specify the encoding error handler for unencodable '
> +          'characters, after a colon (":").  Acceptable values are the same '
> +          'as for the "error" parameter of Python\'s ``encode`` string '
> +          'method.  Default is "%s".' % default_error_encoding_error_handler,
> +          ['--error-encoding', '-e'],
> +          {'action': 'callback', 'callback': set_encoding_and_error_handler,
> +           'metavar': '<name[:handler]>', 'type': 'string',
> +           'dest': 'error_encoding', 'default': 'ascii'}),
> +         (SUPPRESS_HELP,                # usually handled by --error-encoding
> +          ['--error_encoding_error_handler'],
> +          {'action': 'callback', 'callback': set_encoding_error_handler,
> +           'type': 'string', 'dest': 'error_encoding_error_handler',
> +           'default': default_error_encoding_error_handler}),
>           ('Specify the language of input text (ISO 639 2-letter identifier).'
>            '  Default is "en" (English).',
>            ['--language', '-l'], {'dest': 'language_code', 'default': 'en',
> @@ -170,53 +288,60 @@
>           ('Show this help message and exit.',
>            ['--help', '-h'], {'action': 'help'}),
>           # Hidden options, for development use only:
> -         (optik.SUPPRESS_HELP,
> -          ['--dump-settings'],
> -          {'action': 'store_true'}),
> -         (optik.SUPPRESS_HELP,
> -          ['--dump-internals'],
> -          {'action': 'store_true'}),
> -         (optik.SUPPRESS_HELP,
> -          ['--dump-transforms'],
> -          {'action': 'store_true'}),
> -         (optik.SUPPRESS_HELP,
> -          ['--dump-pseudo-xml'],
> -          {'action': 'store_true'}),
> -         (optik.SUPPRESS_HELP,
> -          ['--expose-internal-attribute'],
> +         (SUPPRESS_HELP, ['--dump-settings'], {'action': 'store_true'}),
> +         (SUPPRESS_HELP, ['--dump-internals'], {'action': 'store_true'}),
> +         (SUPPRESS_HELP, ['--dump-transforms'], {'action': 'store_true'}),
> +         (SUPPRESS_HELP, ['--dump-pseudo-xml'], {'action': 'store_true'}),
> +         (SUPPRESS_HELP, ['--expose-internal-attribute'],
>            {'action': 'append', 'dest': 'expose_internals'}),))
>      """Runtime settings and command-line options common to all Docutils front
>      ends.  Setting specs specific to individual Docutils components are also
>      used (see `populate_from_components()`)."""
>  
> +    settings_defaults = {'_disable_config': None}
> +    """Defaults for settings that don't have command-line option equivalents."""
> +
>      relative_path_settings = ('warning_stream',)
>  
>      version_template = '%%prog (Docutils %s)' % docutils.__version__
>      """Default version message."""
>  
> -    def __init__(self, components=(), *args, **kwargs):
> +    def __init__(self, components=(), defaults=None, read_config_files=None,
> +                 *args, **kwargs):
>          """
>          `components` is a list of Docutils components each containing a
>          ``.settings_spec`` attribute.  `defaults` is a mapping of setting
>          default overrides.
>          """
> -        optik.OptionParser.__init__(
> -            self, help=None,
> -            format=optik.Titled(),
> -            # Needed when Optik is updated (replaces above 2 lines):
> -            #self, add_help=None,
> -            #formatter=optik.TitledHelpFormatter(width=78),
> +        optparse.OptionParser.__init__(
> +            self, add_help_option=None,
> +            formatter=optparse.TitledHelpFormatter(width=78),
>              *args, **kwargs)
>          if not self.version:
>              self.version = self.version_template
> -        # Internal settings with no defaults from settings specifications;
> -        # initialize manually:
> -        self.set_defaults(_source=None, _destination=None)
>          # Make an instance copy (it will be modified):
>          self.relative_path_settings = list(self.relative_path_settings)
> -        self.populate_from_components(tuple(components) + (self,))
> +        self.populate_from_components((self,) + tuple(components))
> +        defaults = defaults or {}
> +        if read_config_files and not self.defaults['_disable_config']:
> +            # @@@ Extract this code into a method, which can be called from
> +            # the read_config_file callback also.
> +            config = ConfigParser()
> +            config.read_standard_files(self)
> +            config_settings = config.get_section('options')
> +            make_paths_absolute(config_settings, self.relative_path_settings)
> +            defaults.update(config_settings)
> +        # Internal settings with no defaults from settings specifications;
> +        # initialize manually:
> +        self.set_defaults(_source=None, _destination=None, **defaults)
>  
>      def populate_from_components(self, components):
> +        """
> +        For each component, first populate from the `SettingsSpec.settings_spec`
> +        structure, then from the `SettingsSpec.settings_defaults` dictionary.
> +        After all components have been processed, check for and populate from
> +        each component's `SettingsSpec.settings_default_overrides` dictionary.
> +        """
>          for component in components:
>              if component is None:
>                  continue
> @@ -227,13 +352,15 @@
>              while i < len(settings_spec):
>                  title, description, option_spec = settings_spec[i:i+3]
>                  if title:
> -                    group = optik.OptionGroup(self, title, description)
> +                    group = optparse.OptionGroup(self, title, description)
>                      self.add_option_group(group)
>                  else:
>                      group = self        # single options
>                  for (help_text, option_strings, kwargs) in option_spec:
>                      group.add_option(help=help_text, *option_strings,
>                                       **kwargs)
> +                if component.settings_defaults:
> +                    self.defaults.update(component.settings_defaults)
>                  i += 3
>          for component in components:
>              if component and component.settings_default_overrides:
> @@ -244,6 +371,8 @@
>              values.report_level = self.check_threshold(values.report_level)
>          if hasattr(values, 'halt_level'):
>              values.halt_level = self.check_threshold(values.halt_level)
> +        if hasattr(values, 'exit_level'):
> +            values.exit_level = self.check_threshold(values.exit_level)
>          values._source, values._destination = self.check_args(args)
>          make_paths_absolute(values.__dict__, self.relative_path_settings,
>                              os.getcwd())
> @@ -262,8 +391,12 @@
>          source = destination = None
>          if args:
>              source = args.pop(0)
> +            if source == '-':           # means stdin
> +                source = None
>          if args:
>              destination = args.pop(0)
> +            if destination == '-':      # means stdout
> +                destination = None
>          if args:
>              self.error('Maximum 2 arguments allowed.')
>          if source and source == destination:
> @@ -281,8 +414,44 @@
>      """Docutils configuration files, using ConfigParser syntax (section
>      'options').  Later files override earlier ones."""
>  
> -    def read_standard_files(self):
> -        self.read(self.standard_config_files)
> +    validation = {
> +        'options':
> +        {'input_encoding': validate_encoding,
> +         'output_encoding': validate_encoding,
> +         'output_encoding_error_handler': validate_encoding_error_handler,
> +         'error_encoding': validate_encoding,
> +         'error_encoding_error_handler': validate_encoding_error_handler}}
> +    """{section: {option: validation function}} mapping, used by
> +    `validate_options`.  Validation functions take two parameters: name and
> +    value.  They return a (possibly modified) value, or raise an exception."""
> +
> +    def read_standard_files(self, option_parser):
> +        self.read(self.standard_config_files, option_parser)
> +
> +    def read(self, filenames, option_parser):
> +        if type(filenames) in types.StringTypes:
> +            filenames = [filenames]
> +        for filename in filenames:
> +            CP.ConfigParser.read(self, filename)
> +            self.validate_options(filename, option_parser)
> +
> +    def validate_options(self, filename, option_parser):
> +        for section in self.validation.keys():
> +            if not self.has_section(section):
> +                continue
> +            for option in self.validation[section].keys():
> +                if self.has_option(section, option):
> +                    value = self.get(section, option)
> +                    validator = self.validation[section][option]
> +                    try:
> +                        new_value = validator(option, value)
> +                    except Exception, error:
> +                        raise (ValueError(
> +                            'Error in config file "%s", section "[%s]":\n'
> +                            '    %s: %s\n        %s = %s'
> +                            % (filename, section, error.__class__.__name__,
> +                               error, option, value)), None, sys.exc_info()[2])
> +                    self.set(section, option, new_value)
>  
>      def optionxform(self, optionstr):
>          """
> 
> 
> === Zope/lib/python/docutils/io.py 1.2 => 1.3 ===
> --- Zope/lib/python/docutils/io.py:1.2	Sat Feb  1 04:26:00 2003
> +++ Zope/lib/python/docutils/io.py	Thu Jul 10 11:49:30 2003
> @@ -13,6 +13,7 @@
>  
>  import sys
>  import locale
> +from types import UnicodeType
>  from docutils import TransformSpec
>  
>  
> @@ -26,20 +27,9 @@
>  
>      default_source_path = None
>  
> -    def __init__(self, settings=None, source=None, source_path=None,
> -                 encoding=None):
> +    def __init__(self, source=None, source_path=None, encoding=None):
>          self.encoding = encoding
> -        """The character encoding for the input source."""
> -
> -        if settings:
> -            if not encoding:
> -                self.encoding = settings.input_encoding
> -            import warnings, traceback
> -            warnings.warn(
> -                'Setting input encoding via a "settings" struct is '
> -                'deprecated; send encoding directly instead.\n%s'
> -                % ''.join(traceback.format_list(traceback.extract_stack()
> -                                                [-3:-1])))
> +        """Text encoding for the input source."""
>  
>          self.source = source
>          """The source of input data."""
> @@ -67,7 +57,8 @@
>  
>              locale.setlocale(locale.LC_ALL, '')
>          """
> -        if self.encoding and self.encoding.lower() == 'unicode':
> +        if (self.encoding and self.encoding.lower() == 'unicode'
> +            or isinstance(data, UnicodeType)):
>              return unicode(data)
>          encodings = [self.encoding, 'utf-8']
>          try:
> @@ -87,8 +78,7 @@
>              if not enc:
>                  continue
>              try:
> -                decoded = unicode(data, enc)
> -                return decoded
> +                return unicode(data, enc)
>              except (UnicodeError, LookupError):
>                  pass
>          raise UnicodeError(
> @@ -106,20 +96,13 @@
>  
>      default_destination_path = None
>  
> -    def __init__(self, settings=None, destination=None, destination_path=None,
> -                 encoding=None):
> +    def __init__(self, destination=None, destination_path=None,
> +                 encoding=None, error_handler='strict'):
>          self.encoding = encoding
> -        """The character encoding for the output destination."""
> +        """Text encoding for the output destination."""
>  
> -        if settings:
> -            if not encoding:
> -                self.encoding = settings.output_encoding
> -            import warnings, traceback
> -            warnings.warn(
> -                'Setting output encoding via a "settings" struct is '
> -                'deprecated; send encoding directly instead.\n%s'
> -                % ''.join(traceback.format_list(traceback.extract_stack()
> -                                                [-3:-1])))
> +        self.error_handler = error_handler or 'strict'
> +        """Text encoding error handler."""
>  
>          self.destination = destination
>          """The destination for output data."""
> @@ -141,7 +124,7 @@
>          if self.encoding and self.encoding.lower() == 'unicode':
>              return data
>          else:
> -            return data.encode(self.encoding or '')
> +            return data.encode(self.encoding, self.error_handler)
>  
>  
>  class FileInput(Input):
> @@ -150,8 +133,8 @@
>      Input for single, simple file-like objects.
>      """
>  
> -    def __init__(self, settings=None, source=None, source_path=None,
> -                 encoding=None, autoclose=1):
> +    def __init__(self, source=None, source_path=None,
> +                 encoding=None, autoclose=1, handle_io_errors=1):
>          """
>          :Parameters:
>              - `source`: either a file-like object (which is read directly), or
> @@ -160,11 +143,22 @@
>              - `autoclose`: close automatically after read (boolean); always
>                false if `sys.stdin` is the source.
>          """
> -        Input.__init__(self, settings, source, source_path, encoding)
> +        Input.__init__(self, source, source_path, encoding)
>          self.autoclose = autoclose
> +        self.handle_io_errors = handle_io_errors
>          if source is None:
>              if source_path:
> -                self.source = open(source_path)
> +                try:
> +                    self.source = open(source_path)
> +                except IOError, error:
> +                    if not handle_io_errors:
> +                        raise
> +                    print >>sys.stderr, '%s: %s' % (error.__class__.__name__,
> +                                                    error)
> +                    print >>sys.stderr, (
> +                        'Unable to open source file for reading (%s).  Exiting.'
> +                        % source_path)
> +                    sys.exit(1)
>              else:
>                  self.source = sys.stdin
>                  self.autoclose = None
> @@ -191,8 +185,9 @@
>      Output for single, simple file-like objects.
>      """
>  
> -    def __init__(self, settings=None, destination=None, destination_path=None,
> -                 encoding=None, autoclose=1):
> +    def __init__(self, destination=None, destination_path=None,
> +                 encoding=None, error_handler='strict', autoclose=1,
> +                 handle_io_errors=1):
>          """
>          :Parameters:
>              - `destination`: either a file-like object (which is written
> @@ -203,10 +198,11 @@
>              - `autoclose`: close automatically after write (boolean); always
>                false if `sys.stdout` is the destination.
>          """
> -        Output.__init__(self, settings, destination, destination_path,
> -                        encoding)
> +        Output.__init__(self, destination, destination_path,
> +                        encoding, error_handler)
>          self.opened = 1
>          self.autoclose = autoclose
> +        self.handle_io_errors = handle_io_errors
>          if destination is None:
>              if destination_path:
>                  self.opened = None
> @@ -220,7 +216,16 @@
>                  pass
>  
>      def open(self):
> -        self.destination = open(self.destination_path, 'w')
> +        try:
> +            self.destination = open(self.destination_path, 'w')
> +        except IOError, error:
> +            if not self.handle_io_errors:
> +                raise
> +            print >>sys.stderr, '%s: %s' % (error.__class__.__name__,
> +                                            error)
> +            print >>sys.stderr, ('Unable to open destination file for writing '
> +                                 '(%s).  Exiting.' % source_path)
> +            sys.exit(1)
>          self.opened = 1
>  
>      def write(self, data):
> 
> 
> === Zope/lib/python/docutils/nodes.py 1.2 => 1.3 ===
> --- Zope/lib/python/docutils/nodes.py:1.2	Sat Feb  1 04:26:00 2003
> +++ Zope/lib/python/docutils/nodes.py	Thu Jul 10 11:49:30 2003
> @@ -53,7 +53,14 @@
>      """The line number (1-based) of the beginning of this Node in `source`."""
>  
>      def __nonzero__(self):
> -        """Node instances are always true."""
> +        """
> +        Node instances are always true, even if they're empty.  A node is more
> +        than a simple container.  Its boolean "truth" does not depend on
> +        having one or more subnodes in the doctree.
> +
> +        Use `len()` to check node length.  Use `None` to represent a boolean
> +        false value.
> +        """
>          return 1
>  
>      def asdom(self, dom=xml.dom.minidom):
> @@ -175,6 +182,9 @@
>              data = repr(self.data[:64] + ' ...')
>          return '<%s: %s>' % (self.tagname, data)
>  
> +    def __len__(self):
> +        return len(self.data)
> +
>      def shortrepr(self):
>          data = repr(self.data)
>          if len(data) > 20:
> @@ -261,9 +271,9 @@
>      def _dom_node(self, domroot):
>          element = domroot.createElement(self.tagname)
>          for attribute, value in self.attributes.items():
> -            if type(value) is ListType:
> -                value = ' '.join(value)
> -            element.setAttribute(attribute, str(value))
> +            if isinstance(value, ListType):
> +                value = ' '.join(['%s' % v for v in value])
> +            element.setAttribute(attribute, '%s' % value)
>          for child in self.children:
>              element.appendChild(child._dom_node(domroot))
>          return element
> @@ -289,10 +299,13 @@
>              return '<%s...>' % self.tagname
>  
>      def __str__(self):
> +        return unicode(self).encode('raw_unicode_escape')
> +
> +    def __unicode__(self):
>          if self.children:
> -            return '%s%s%s' % (self.starttag(),
> -                                ''.join([str(c) for c in self.children]),
> -                                self.endtag())
> +            return u'%s%s%s' % (self.starttag(),
> +                                 ''.join([str(c) for c in self.children]),
> +                                 self.endtag())
>          else:
>              return self.emptytag()
>  
> @@ -302,19 +315,19 @@
>              if value is None:           # boolean attribute
>                  parts.append(name)
>              elif isinstance(value, ListType):
> -                values = [str(v) for v in value]
> +                values = ['%s' % v for v in value]
>                  parts.append('%s="%s"' % (name, ' '.join(values)))
>              else:
> -                parts.append('%s="%s"' % (name, str(value)))
> +                parts.append('%s="%s"' % (name, value))
>          return '<%s>' % ' '.join(parts)
>  
>      def endtag(self):
>          return '</%s>' % self.tagname
>  
>      def emptytag(self):
> -        return '<%s/>' % ' '.join([self.tagname] +
> -                                  ['%s="%s"' % (n, v)
> -                                   for n, v in self.attlist()])
> +        return u'<%s/>' % ' '.join([self.tagname] +
> +                                    ['%s="%s"' % (n, v)
> +                                     for n, v in self.attlist()])
>  
>      def __len__(self):
>          return len(self.children)
> @@ -619,6 +632,10 @@
>          self.substitution_defs = {}
>          """Mapping of substitution names to substitution_definition nodes."""
>  
> +        self.substitution_names = {}
> +        """Mapping of case-normalized substitution names to case-sensitive
> +        names."""
> +
>          self.refnames = {}
>          """Mapping of names to lists of referencing nodes."""
>  
> @@ -864,8 +881,8 @@
>          self.citation_refs.setdefault(ref['refname'], []).append(ref)
>          self.note_refname(ref)
>  
> -    def note_substitution_def(self, subdef, msgnode=None):
> -        name = subdef['name']
> +    def note_substitution_def(self, subdef, def_name, msgnode=None):
> +        name = subdef['name'] = whitespace_normalize_name(def_name)
>          if self.substitution_defs.has_key(name):
>              msg = self.reporter.error(
>                    'Duplicate substitution definition name: "%s".' % name,
> @@ -874,12 +891,14 @@
>                  msgnode += msg
>              oldnode = self.substitution_defs[name]
>              dupname(oldnode)
> -        # keep only the last definition
> +        # keep only the last definition:
>          self.substitution_defs[name] = subdef
> +        # case-insensitive mapping:
> +        self.substitution_names[fully_normalize_name(name)] = name
>  
> -    def note_substitution_ref(self, subref):
> -        self.substitution_refs.setdefault(
> -              subref['refname'], []).append(subref)
> +    def note_substitution_ref(self, subref, refname):
> +        name = subref['refname'] = whitespace_normalize_name(refname)
> +        self.substitution_refs.setdefault(name, []).append(subref)
>  
>      def note_pending(self, pending, priority=None):
>          self.transformer.add_pending(pending, priority)
> @@ -908,6 +927,7 @@
>  
>  class title(Titular, PreBibliographic, TextElement): pass
>  class subtitle(Titular, PreBibliographic, TextElement): pass
> +class rubric(Titular, TextElement): pass
>  
>  
>  # ========================
> @@ -947,13 +967,30 @@
>  
>      """
>      Topics are terminal, "leaf" mini-sections, like block quotes with titles,
> -    or textual figures. A topic is just like a section, except that it has no
> +    or textual figures.  A topic is just like a section, except that it has no
>      subsections, and it doesn't have to conform to section placement rules.
>  
>      Topics are allowed wherever body elements (list, table, etc.) are allowed,
> -    but only at the top level of a section or document. Topics cannot nest
> -    inside topics or body elements; you can't have a topic inside a table,
> -    list, block quote, etc.
> +    but only at the top level of a section or document.  Topics cannot nest
> +    inside topics, sidebars, or body elements; you can't have a topic inside a
> +    table, list, block quote, etc.
> +    """
> +
> +
> +class sidebar(Structural, Element):
> +
> +    """
> +    Sidebars are like miniature, parallel documents that occur inside other
> +    documents, providing related or reference material.  A sidebar is
> +    typically offset by a border and "floats" to the side of the page; the
> +    document's main text may flow around it.  Sidebars can also be likened to
> +    super-footnotes; their content is outside of the flow of the document's
> +    main text.
> +
> +    Sidebars are allowed wherever body elements (list, table, etc.) are
> +    allowed, but only at the top level of a section or document.  Sidebars
> +    cannot nest inside sidebars, topics, or body elements; you can't have a
> +    sidebar inside a table, list, block quote, etc.
>      """
>  
>  
> @@ -1009,6 +1046,7 @@
>  class doctest_block(General, FixedTextElement): pass
>  class line_block(General, FixedTextElement): pass
>  class block_quote(General, Element): pass
> +class attribution(Part, TextElement): pass
>  class attention(Admonition, Element): pass
>  class caution(Admonition, Element): pass
>  class danger(Admonition, Element): pass
> @@ -1018,6 +1056,7 @@
>  class tip(Admonition, Element): pass
>  class hint(Admonition, Element): pass
>  class warning(Admonition, Element): pass
> +class admonition(Admonition, Element): pass
>  class comment(Special, Invisible, PreBibliographic, FixedTextElement): pass
>  class substitution_definition(Special, Invisible, TextElement): pass
>  class target(Special, Invisible, Inline, TextElement, Targetable): pass
> @@ -1050,8 +1089,8 @@
>  
>      def astext(self):
>          line = self.get('line', '')
> -        return '%s:%s: (%s/%s) %s' % (self['source'], line, self['type'],
> -                                      self['level'], Element.astext(self))
> +        return u'%s:%s: (%s/%s) %s' % (self['source'], line, self['type'],
> +                                       self['level'], Element.astext(self))
>  
>  
>  class pending(Special, Invisible, PreBibliographic, Element):
> @@ -1106,7 +1145,7 @@
>                  internals.append('%7s%s:' % ('', key))
>                  internals.extend(['%9s%s' % ('', line)
>                                    for line in value.pformat().splitlines()])
> -            elif value and type(value) == ListType \
> +            elif value and isinstance(value, ListType) \
>                    and isinstance(value[0], Node):
>                  internals.append('%7s%s:' % ('', key))
>                  for v in value:
> @@ -1146,6 +1185,8 @@
>  class title_reference(Inline, TextElement): pass
>  class abbreviation(Inline, TextElement): pass
>  class acronym(Inline, TextElement): pass
> +class superscript(Inline, TextElement): pass
> +class subscript(Inline, TextElement): pass
>  
>  
>  class image(General, Inline, TextElement):
> @@ -1154,6 +1195,7 @@
>          return self.get('alt', '')
>  
>  
> +class inline(Inline, TextElement): pass
>  class problematic(Inline, TextElement): pass
>  class generated(Inline, TextElement): pass
>  
> @@ -1164,7 +1206,8 @@
>  
>  node_class_names = """
>      Text
> -    abbreviation acronym address attention author authors
> +    abbreviation acronym address admonition attention attribution author
> +        authors
>      block_quote bullet_list
>      caption caution citation citation_reference classifier colspec comment
>          contact copyright
> @@ -1175,15 +1218,15 @@
>          footnote footnote_reference
>      generated
>      header hint
> -    image important
> +    image important inline
>      label legend line_block list_item literal literal_block
>      note
>      option option_argument option_group option_list option_list_item
>          option_string organization
>      paragraph pending problematic
> -    raw reference revision row
> -    section status strong substitution_definition substitution_reference
> -        subtitle system_message
> +    raw reference revision row rubric
> +    section sidebar status strong subscript substitution_definition
> +        substitution_reference subtitle superscript system_message
>      table target tbody term tgroup thead tip title title_reference topic
>          transition
>      version
> @@ -1248,11 +1291,14 @@
>      subclasses), subclass `NodeVisitor` instead.
>      """
>  
> -    # Save typing with dynamic definitions.
> -    for name in node_class_names:
> -        exec """def visit_%s(self, node): pass\n""" % name
> -        exec """def depart_%s(self, node): pass\n""" % name
> -    del name
> +def _nop(self, node):
> +    pass
> +
> +# Save typing with dynamic assignments:
> +for _name in node_class_names:
> +    setattr(SparseNodeVisitor, "visit_" + _name, _nop)
> +    setattr(SparseNodeVisitor, "depart_" + _name, _nop)
> +del _name, _nop
>  
>  
>  class GenericNodeVisitor(NodeVisitor):
> @@ -1281,13 +1327,17 @@
>          """Override for generic, uniform traversals."""
>          raise NotImplementedError
>  
> -    # Save typing with dynamic definitions.
> -    for name in node_class_names:
> -        exec """def visit_%s(self, node):
> -                    self.default_visit(node)\n""" % name
> -        exec """def depart_%s(self, node):
> -                    self.default_departure(node)\n""" % name
> -    del name
> +def _call_default_visit(self, node):
> +    self.default_visit(node)
> +
> +def _call_default_departure(self, node):
> +    self.default_departure(node)
> +
> +# Save typing with dynamic assignments:
> +for _name in node_class_names:
> +    setattr(GenericNodeVisitor, "visit_" + _name, _call_default_visit)
> +    setattr(GenericNodeVisitor, "depart_" + _name, _call_default_departure)
> +del _name, _call_default_visit, _call_default_departure
>  
>  
>  class TreeCopyVisitor(GenericNodeVisitor):
> @@ -1385,9 +1435,9 @@
>      Convert `string` into an identifier and return it.
>  
>      Docutils identifiers will conform to the regular expression
> -    ``[a-z][-a-z0-9]*``. For CSS compatibility, identifiers (the "class" and
> -    "id" attributes) should have no underscores, colons, or periods. Hyphens
> -    may be used.
> +    ``[a-z](-?[a-z0-9]+)*``.  For CSS compatibility, identifiers (the "class"
> +    and "id" attributes) should have no underscores, colons, or periods.
> +    Hyphens may be used.
>  
>      - The `HTML 4.01 spec`_ defines identifiers based on SGML tokens:
>  
> @@ -1410,7 +1460,7 @@
>      these characters. They should be replaced with hyphens ("-"). Combined
>      with HTML's requirements (the first character must be a letter; no
>      "unicode", "latin1", or "escape" characters), this results in the
> -    ``[a-z][-a-z0-9]*`` pattern.
> +    ``[a-z](-?[a-z0-9]+)*`` pattern.
>  
>      .. _HTML 4.01 spec: http://www.w3.org/TR/html401
>      .. _CSS1 spec: http://www.w3.org/TR/REC-CSS1
> @@ -1425,3 +1475,11 @@
>  def dupname(node):
>      node['dupname'] = node['name']
>      del node['name']
> +
> +def fully_normalize_name(name):
> +    """Return a case- and whitespace-normalized name."""
> +    return ' '.join(name.lower().split())
> +
> +def whitespace_normalize_name(name):
> +    """Return a whitespace-normalized name."""
> +    return ' '.join(name.split())
> 
> 
> === Zope/lib/python/docutils/statemachine.py 1.2 => 1.3 ===
> --- Zope/lib/python/docutils/statemachine.py:1.2	Sat Feb  1 04:26:00 2003
> +++ Zope/lib/python/docutils/statemachine.py	Thu Jul 10 11:49:30 2003
> @@ -266,7 +266,8 @@
>                      transitions = None
>                  state = self.get_state(next_state)
>          except:
> -            self.error()
> +            if self.debug:
> +                self.error()
>              raise
>          self.observers = []
>          return results
> @@ -1294,11 +1295,11 @@
>  
>      """A `ViewList` with string-specific methods."""
>  
> -    def strip_indent(self, length, start=0, end=sys.maxint):
> +    def trim_left(self, length, start=0, end=sys.maxint):
>          """
> -        Strip `length` characters off the beginning of each item, in-place,
> +        Trim `length` characters off the beginning of each item, in-place,
>          from index `start` to `end`.  No whitespace-checking is done on the
> -        stripped text.  Does not affect slice parent.
> +        trimmed text.  Does not affect slice parent.
>          """
>          self.data[start:end] = [line[length:]
>                                  for line in self.data[start:end]]
> @@ -1381,8 +1382,19 @@
>          if first_indent is not None and block:
>              block.data[0] = block.data[0][first_indent:]
>          if indent and strip_indent:
> -            block.strip_indent(indent, start=(first_indent is not None))
> +            block.trim_left(indent, start=(first_indent is not None))
>          return block, indent or 0, blank_finish
> +
> +    def get_2D_block(self, top, left, bottom, right, strip_indent=1):
> +        block = self[top:bottom]
> +        indent = right
> +        for i in range(len(block.data)):
> +            block.data[i] = line = block.data[i][left:right].rstrip()
> +            if line:
> +                indent = min(indent, len(line) - len(line.lstrip()))
> +        if strip_indent and 0 < indent < right:
> +            block.data = [line[indent:] for line in block.data]
> +        return block
>  
>  
>  class StateMachineError(Exception): pass
> 
> 
> === Zope/lib/python/docutils/utils.py 1.2 => 1.3 ===
> --- Zope/lib/python/docutils/utils.py:1.2	Sat Feb  1 04:26:00 2003
> +++ Zope/lib/python/docutils/utils.py	Thu Jul 10 11:49:30 2003
> @@ -20,8 +20,9 @@
>  
>  class SystemMessage(ApplicationError):
>  
> -    def __init__(self, system_message):
> +    def __init__(self, system_message, level):
>          Exception.__init__(self, system_message.astext())
> +        self.level = level
>  
>  
>  class Reporter:
> @@ -75,7 +76,7 @@
>      """List of names for system message levels, indexed by level."""
>  
>      def __init__(self, source, report_level, halt_level, stream=None,
> -                 debug=0):
> +                 debug=0, encoding='ascii', error_handler='replace'):
>          """
>          Initialize the `ConditionSet` forthe `Reporter`'s default category.
>  
> @@ -90,6 +91,8 @@
>              - `stream`: Where warning output is sent.  Can be file-like (has a
>                ``.write`` method), a string (file name, opened for writing), or
>                `None` (implies `sys.stderr`; default).
> +            - `encoding`: The encoding for stderr output.
> +            - `error_handler`: The error handler for stderr output encoding.
>          """
>          self.source = source
>          """The path to or description of the source data."""
> @@ -99,6 +102,12 @@
>          elif type(stream) in (StringType, UnicodeType):
>              raise NotImplementedError('This should open a file for writing.')
>  
> +        self.encoding = encoding
> +        """The character encoding for the stderr output."""
> +
> +        self.error_handler = error_handler
> +        """The character encoding error handler."""
> +
>          self.categories = {'': ConditionSet(debug, report_level, halt_level,
>                                              stream)}
>          """Mapping of category names to conditions. Default category is ''."""
> @@ -107,6 +116,9 @@
>          """List of bound methods or functions to call with each system_message
>          created."""
>  
> +        self.max_level = -1
> +        """The highest level system message generated so far."""
> +
>      def set_conditions(self, category, report_level, halt_level,
>                         stream=None, debug=0):
>          if stream is None:
> @@ -164,14 +176,16 @@
>                                     *children, **attributes)
>          debug, report_level, halt_level, stream = self[category].astuple()
>          if level >= report_level or debug and level == 0:
> +            msgtext = msg.astext().encode(self.encoding, self.error_handler)
>              if category:
> -                print >>stream, msg.astext(), '[%s]' % category
> +                print >>stream, msgtext, '[%s]' % category
>              else:
> -                print >>stream, msg.astext()
> +                print >>stream, msgtext
>          if level >= halt_level:
> -            raise SystemMessage(msg)
> +            raise SystemMessage(msg, level)
>          if level > 0 or debug:
>              self.notify_observers(msg)
> +        self.max_level = max(level, self.max_level)
>          return msg
>  
>      def debug(self, *args, **kwargs):
> @@ -368,10 +382,6 @@
>          attlist.append((attname.lower(), data))
>      return attlist
>  
> -def normalize_name(name):
> -    """Return a case- and whitespace-normalized name."""
> -    return ' '.join(name.lower().split())
> -
>  def new_document(source, settings=None):
>      """
>      Return a new empty document object.
> @@ -385,7 +395,9 @@
>      if settings is None:
>          settings = frontend.OptionParser().get_default_values()
>      reporter = Reporter(source, settings.report_level, settings.halt_level,
> -                        settings.warning_stream, settings.debug)
> +                        stream=settings.warning_stream, debug=settings.debug,
> +                        encoding=settings.error_encoding,
> +                        error_handler=settings.error_encoding_error_handler)
>      document = nodes.document(settings, reporter, source=source)
>      document.note_source(source, -1)
>      return document
> @@ -401,7 +413,7 @@
>  
>  def relative_path(source, target):
>      """
> -    Build and return a path to `target`, relative to `source`.
> +    Build and return a path to `target`, relative to `source` (both files).
>  
>      If there is no common prefix, return the absolute path to `target`.
>      """
> @@ -426,7 +438,7 @@
>  def get_source_line(node):
>      """
>      Return the "source" and "line" attributes from the `node` given or from
> -    it's closest ancestor.
> +    its closest ancestor.
>      """
>      while node:
>          if node.source or node.line:
> 
> 
> _______________________________________________
> Zope-Checkins maillist  -  Zope-Checkins@zope.org
> http://mail.zope.org/mailman/listinfo/zope-checkins