Commits

Anonymous committed a35896d

updated docutils

Comments (0)

Files changed (74)

docutils/__init__.py

-# $Id: __init__.py 6164 2009-10-11 11:00:11Z grubert $
+# $Id: __init__.py 7087 2011-07-08 11:27:20Z grubert $
 # Author: David Goodger <goodger@python.org>
 # Copyright: This module has been placed in the public domain.
 
 
 __docformat__ = 'reStructuredText'
 
-__version__ = '0.6'
+__version__ = '0.8'
 """``major.minor.micro`` version number.  The micro number is bumped for API
 changes, for new functionality, and for interim project releases.  The minor
 number is bumped whenever there is a significant project release.  The major
 """Extra version details (e.g. 'snapshot 2005-05-29, r3410', 'repository',
 'release'), modified automatically & manually."""
 
-class ApplicationError(StandardError): pass
+import sys
+
+class ApplicationError(StandardError):
+    # Workaround:
+    # In Python < 2.6, unicode(<exception instance>) calls `str` on the
+    # arg and therefore, e.g., unicode(StandardError(u'\u234')) fails
+    # with UnicodeDecodeError.
+    if sys.version_info < (2,6):
+        def __unicode__(self):
+            return u', '.join(self.args)
+
 class DataError(ApplicationError): pass
 
 
-# $Id: core.py 6119 2009-09-09 09:21:59Z milde $
+# $Id: core.py 7070 2011-07-05 10:13:17Z milde $
 # Author: David Goodger <goodger@python.org>
 # Copyright: This module has been placed in the public domain.
 
 from docutils import frontend, io, utils, readers, writers
 from docutils.frontend import OptionParser
 from docutils.transforms import Transformer
+from docutils.error_reporting import ErrorOutput, ErrorString
 import docutils.readers.doctree
 
-
 class Publisher:
 
     """
         """An object containing Docutils settings as instance attributes.
         Set by `self.process_command_line()` or `self.get_settings()`."""
 
+        self._stderr = ErrorOutput()
+
     def set_reader(self, reader_name, parser, parser_name):
         """Set `self.reader` by name."""
         reader_class = readers.get_reader_class(reader_name)
             usage, description, settings_spec, config_section, **defaults)
         if argv is None:
             argv = sys.argv[1:]
+            # converting to Unicode (Python 3 does this automatically):
+            if sys.version_info < (3,0):
+                # TODO: make this failsafe and reversible
+                argv_encoding = (sys.stdin.encoding or
+                                 frontend.locale_encoding or 'ascii')
+                argv = [a.decode(argv_encoding) for a in argv]
         self.settings = option_parser.parse_args(argv)
 
     def set_io(self, source_path=None, destination_path=None):
         if not self.document:
             return
         if self.settings.dump_settings:
-            print >>sys.stderr, '\n::: Runtime settings:'
-            print >>sys.stderr, pprint.pformat(self.settings.__dict__)
+            print >>self._stderr, '\n::: Runtime settings:'
+            print >>self._stderr, pprint.pformat(self.settings.__dict__)
         if self.settings.dump_internals:
-            print >>sys.stderr, '\n::: Document internals:'
-            print >>sys.stderr, pprint.pformat(self.document.__dict__)
+            print >>self._stderr, '\n::: Document internals:'
+            print >>self._stderr, pprint.pformat(self.document.__dict__)
         if self.settings.dump_transforms:
-            print >>sys.stderr, '\n::: Transforms applied:'
-            print >>sys.stderr, (' (priority, transform class, '
+            print >>self._stderr, '\n::: Transforms applied:'
+            print >>self._stderr, (' (priority, transform class, '
                                  'pending node details, keyword args)')
-            print >>sys.stderr, pprint.pformat(
+            print >>self._stderr, pprint.pformat(
                 [(priority, '%s.%s' % (xclass.__module__, xclass.__name__),
                   pending and pending.details, kwargs)
                  for priority, xclass, pending, kwargs
                  in self.document.transformer.applied])
         if self.settings.dump_pseudo_xml:
-            print >>sys.stderr, '\n::: Pseudo-XML:'
-            print >>sys.stderr, self.document.pformat().encode(
+            print >>self._stderr, '\n::: Pseudo-XML:'
+            print >>self._stderr, self.document.pformat().encode(
                 'raw_unicode_escape')
 
     def report_Exception(self, error):
         elif isinstance(error, UnicodeEncodeError):
             self.report_UnicodeError(error)
         else:
-            print >>sys.stderr, '%s: %s' % (error.__class__.__name__, error)
-            print >>sys.stderr, ("""\
+            print >>self._stderr, u'%s' % ErrorString(error)
+            print >>self._stderr, ("""\
 Exiting due to error.  Use "--traceback" to diagnose.
 Please report errors to <docutils-users@lists.sf.net>.
 Include "--traceback" output, Docutils version (%s [%s]),
                          sys.version.split()[0]))
 
     def report_SystemMessage(self, error):
-        print >>sys.stderr, ('Exiting due to level-%s (%s) system message.'
+        print >>self._stderr, ('Exiting due to level-%s (%s) system message.'
                              % (error.level,
                                 utils.Reporter.levels[error.level]))
 
     def report_UnicodeError(self, error):
-        sys.stderr.write(
-            '%s: %s\n'
+        data = error.object[error.start:error.end]
+        self._stderr.write(
+            '%s\n'
             '\n'
             'The specified output encoding (%s) cannot\n'
             'handle all of the output.\n'
             'Try setting "--output-encoding-error-handler" to\n'
             '\n'
             '* "xmlcharrefreplace" (for HTML & XML output);\n'
-            % (error.__class__.__name__, error,
-               self.settings.output_encoding))
-        try:
-            data = error.object[error.start:error.end]
-            sys.stderr.write(
-                '  the output will contain "%s" and should be usable.\n'
-                '* "backslashreplace" (for other output formats, Python 2.3+);\n'
-                '  look for "%s" in the output.\n'
-                % (data.encode('ascii', 'xmlcharrefreplace'),
-                   data.encode('ascii', 'backslashreplace')))
-        except AttributeError:
-            sys.stderr.write('  the output should be usable as-is.\n')
-        sys.stderr.write(
+            '  the output will contain "%s" and should be usable.\n'
+            '* "backslashreplace" (for other output formats);\n'
+            '  look for "%s" in the output.\n'
             '* "replace"; look for "?" in the output.\n'
             '\n'
             '"--output-encoding-error-handler" is currently set to "%s".\n'
             'Include "--traceback" output, Docutils version (%s),\n'
             'Python version (%s), your OS type & version, and the\n'
             'command line used.\n'
-            % (self.settings.output_encoding_error_handler,
+            % (ErrorString(error),
+               self.settings.output_encoding,
+               data.encode('ascii', 'xmlcharrefreplace'),
+               data.encode('ascii', 'backslashreplace'),
+               self.settings.output_encoding_error_handler,
                __version__, sys.version.split()[0]))
 
 default_usage = '%prog [options] [<source> [<destination>]]'

docutils/error_reporting.py

+#!/usr/bin/env python
+# -*- coding: utf8 -*-
+
+# :Id: $Id: error_reporting.py 7073 2011-07-07 06:49:19Z milde $
+# :Copyright: © 2011 Günter Milde.
+# :License: Released under the terms of the `2-Clause BSD license`_, in short:
+# 
+#    Copying and distribution of this file, with or without modification,
+#    are permitted in any medium without royalty provided the copyright
+#    notice and this notice are preserved.
+#    This file is offered as-is, without any warranty.
+# 
+# .. _2-Clause BSD license: http://www.spdx.org/licenses/BSD-2-Clause
+
+"""
+Error reporting should be safe from encoding/decoding errors.
+However, implicit conversions of strings and exceptions like
+
+>>> u'%s world: %s' % ('H\xe4llo', Exception(u'H\xe4llo')
+
+fail in some Python versions:
+
+* In Python <= 2.6, ``unicode(<exception instance>)`` uses
+  `__str__` and fails with non-ASCII chars in`unicode` arguments.
+  (work around http://bugs.python.org/issue2517):
+
+* In Python 2, unicode(<exception instance>) fails, with non-ASCII
+  chars in arguments. (Use case: in some locales, the errstr
+  argument of IOError contains non-ASCII chars.)
+
+* In Python 2, str(<exception instance>) fails, with non-ASCII chars
+  in `unicode` arguments.
+
+The `SafeString`, `ErrorString` and `ErrorOutput` classes handle
+common exceptions.
+"""
+
+import sys, codecs
+
+# Guess the locale's encoding.
+# If no valid guess can be made, locale_encoding is set to `None`:
+try:
+    import locale # module missing in Jython
+except ImportError:
+    locale_encoding = None
+else:
+    locale_encoding = locale.getlocale()[1] or locale.getdefaultlocale()[1]
+    # locale.getpreferredencoding([do_setlocale=True|False])
+    # has side-effects | might return a wrong guess.
+    # (cf. Update 1 in http://stackoverflow.com/questions/4082645/using-python-2-xs-locale-module-to-format-numbers-and-currency)
+    try:
+        codecs.lookup(locale_encoding or '') # None -> ''
+    except LookupError:
+        locale_encoding = None
+
+
+
+class SafeString(object):
+    """
+    A wrapper providing robust conversion to `str` and `unicode`.
+    """
+
+    def __init__(self, data, encoding=None, encoding_errors='backslashreplace',
+                 decoding_errors='replace'):
+        self.data = data
+        self.encoding = (encoding or getattr(data, 'encoding', None) or
+                         locale_encoding or 'ascii')
+        self.encoding_errors = encoding_errors
+        self.decoding_errors = decoding_errors
+
+
+    def __str__(self):
+        try:
+            return str(self.data)
+        except UnicodeEncodeError, err:
+            if isinstance(self.data, Exception):
+                args = [str(SafeString(arg, self.encoding,
+                                        self.encoding_errors))
+                        for arg in self.data.args]
+                return ', '.join(args)
+            if isinstance(self.data, unicode):
+                return self.data.encode(self.encoding, self.encoding_errors)
+            raise
+
+    def __unicode__(self):
+        """
+        Return unicode representation of `self.data`.
+
+        Try ``unicode(self.data)``, catch `UnicodeError` and
+
+        * if `self.data` is an Exception instance, work around
+          http://bugs.python.org/issue2517 with an emulation of
+          Exception.__unicode__,
+
+        * else decode with `self.encoding` and `self.decoding_errors`.
+        """
+        try:
+            return unicode(self.data)
+        except UnicodeError, error: # catch ..Encode.. and ..Decode.. errors
+            if isinstance(self.data, EnvironmentError):
+                return  u"[Errno %s] %s: '%s'" % (self.data.errno,
+                    SafeString(self.data.strerror, self.encoding,
+                            self.decoding_errors),
+                    SafeString(self.data.filename, self.encoding,
+                            self.decoding_errors))
+            if isinstance(self.data, Exception):
+                args = [unicode(SafeString(arg, self.encoding,
+                            decoding_errors=self.decoding_errors))
+                        for arg in self.data.args]
+                return u', '.join(args)
+            if isinstance(error, UnicodeDecodeError):
+                return unicode(self.data, self.encoding, self.decoding_errors)
+            raise
+
+class ErrorString(SafeString):
+    """
+    Safely report exception type and message.
+    """
+    def __str__(self):
+        return '%s: %s' % (self.data.__class__.__name__,
+                            super(ErrorString, self).__str__())
+
+    def __unicode__(self):
+        return u'%s: %s' % (self.data.__class__.__name__,
+                            super(ErrorString, self).__unicode__())
+
+
+class ErrorOutput(object):
+    """
+    Wrapper class for file-like error streams with
+    failsave de- and encoding of `str`, `bytes`, `unicode` and
+    `Exception` instances.
+    """
+
+    def __init__(self, stream=None, encoding=None,
+                 encoding_errors='backslashreplace',
+                 decoding_errors='replace'):
+        """
+        :Parameters:
+            - `stream`: a file-like object (which is written to),
+                        a string (opended as a file),
+                        `None` (bind to `sys.stderr`; default).
+                        If evaluating to `False` (but not `None`),
+                        write() requests are ignored.
+            - `encoding`: `stream` text encoding. Guessed if None.
+            - `encoding_errors`: how to treat encoding errors.
+        """
+        if stream is None:
+            stream = sys.stderr
+        elif not(stream):
+            stream = False
+        # if `stream` is a file name, open it
+        elif isinstance(stream, str):
+            stream = open(stream, 'w')
+        elif isinstance(stream, unicode):
+            stream = open(stream.encode(sys.getfilesystemencoding()), 'w')
+
+        self.stream = stream
+        """Where warning output is sent."""
+
+        self.encoding = (encoding or getattr(stream, 'encoding', None) or
+                         locale_encoding or 'ascii')
+        """The output character encoding."""
+
+        self.encoding_errors = encoding_errors
+        """Encoding error handler."""
+
+        self.decoding_errors = decoding_errors
+        """Decoding error handler."""
+
+    def write(self, data):
+        """
+        Write `data` to self.stream. Ignore, if self.stream is False.
+
+        `data` can be a `string`, `unicode`, or `Exception` instance.
+        """
+        if self.stream is False:
+            return
+        if isinstance(data, Exception):
+            data = unicode(SafeString(data, self.encoding,
+                                  self.encoding_errors, self.decoding_errors))
+        try:
+            self.stream.write(data)
+        except UnicodeEncodeError:
+            self.stream.write(data.encode(self.encoding, self.encoding_errors))
+        except TypeError: # in Python 3, stderr expects unicode
+            self.stream.write(unicode(data, self.encoding, self.decoding_errors))
+
+    def close(self):
+        """
+        Close the error-output stream.
+
+        Ignored if the stream is` sys.stderr` or `sys.stdout` or has no 
+        close() method.
+        """
+        if self.stream in (sys.stdout, sys.stderr):
+            return
+        try:
+            self.stream.close()
+        except AttributeError:
+            pass
+

docutils/frontend.py

-# $Id: frontend.py 6154 2009-10-05 19:08:10Z milde $
+# $Id: frontend.py 7071 2011-07-05 10:13:46Z milde $
 # Author: David Goodger <goodger@python.org>
 # Copyright: This module has been placed in the public domain.
 
   `validate_threshold`, `validate_colon_separated_string_list`,
   `validate_dependency_file`.
 * `make_paths_absolute`.
+* SettingSpec manipulation: `filter_settings_spec`.
 """
 
 __docformat__ = 'reStructuredText'
 import warnings
 import ConfigParser as CP
 import codecs
+import optparse
+from optparse import SUPPRESS_HELP
 import docutils
 import docutils.utils
 import docutils.nodes
-import optparse
-from optparse import SUPPRESS_HELP
+from docutils.error_reporting import locale_encoding, ErrorOutput
 
 
 def store_multiple(option, opt, value, parser, *args, **kwargs):
                                     config_parser=None, config_section=None):
     try:
         codecs.lookup_error(value)
-    except AttributeError:    # TODO: remove (only needed prior to Python 2.3)
-        if value not in ('strict', 'ignore', 'replace', 'xmlcharrefreplace'):
-            raise (LookupError(
-                'unknown encoding error handler: "%s" (choices: '
-                '"strict", "ignore", "replace", or "xmlcharrefreplace")' % value),
-                   None, sys.exc_info()[2])
     except LookupError:
         raise (LookupError(
             'unknown encoding error handler: "%s" (choices: '
 def make_one_path_absolute(base_path, path):
     return os.path.abspath(os.path.join(base_path, path))
 
+def filter_settings_spec(settings_spec, *exclude, **replace):
+    """Return a copy of `settings_spec` excluding/replacing some settings.
+
+    `settings_spec` is a tuple of configuration settings with a structure
+    described for docutils.SettingsSpec.settings_spec.
+
+    Optional positional arguments are names of to-be-excluded settings.
+    Keyword arguments are option specification replacements.
+    (See the html4strict writer for an example.)
+    """
+    settings = list(settings_spec)
+    # every third item is a sequence of option tuples
+    for i in range(2, len(settings), 3):
+        newopts = []
+        for opt_spec in settings[i]:
+            # opt_spec is ("<help>", [<option strings>], {<keyword args>})
+            opt_name = [opt_string[2:].replace('-', '_')
+                        for opt_string in opt_spec[1]
+                            if opt_string.startswith('--')
+                       ][0]
+            if opt_name in exclude:
+                continue
+            if opt_name in replace.keys():
+                newopts.append(replace[opt_name])
+            else:
+                newopts.append(opt_spec)
+        settings[i] = tuple(newopts)
+    return tuple(settings)
+
 
 class Values(optparse.Values):
 
                     new_value = self.validator(setting, value, parser)
                 except Exception, error:
                     raise (optparse.OptionValueError(
-                        'Error in option "%s":\n    %s: %s'
-                        % (opt, error.__class__.__name__, error)),
+                        'Error in option "%s":\n    %s'
+                        % (opt, ErrorString(error))),
                            None, sys.exc_info()[2])
                 setattr(values, setting, new_value)
             if self.overrides:
               '0': 0, 'off': 0, 'no': 0, 'false': 0, '': 0}
     """Lookup table for boolean configuration file settings."""
 
-    try:
-        default_error_encoding = sys.stderr.encoding or 'ascii'
-    except AttributeError:
-        default_error_encoding = 'ascii'
+    default_error_encoding = getattr(sys.stderr, 'encoding',
+                                     None) or locale_encoding or 'ascii'
 
-    # TODO: variable no longer needed since 'backslashreplace' is
-    # part of Python >= 2.3 (required since Docutils 0.6)
-    if hasattr(codecs, 'backslashreplace_errors'):
-        default_error_encoding_error_handler = 'backslashreplace'
-    else:
-        default_error_encoding_error_handler = 'replace'
+    default_error_encoding_error_handler = 'backslashreplace'
 
     settings_spec = (
         'General Docutils Options',
           ['--error-encoding-error-handler'],
           {'default': default_error_encoding_error_handler,
            'validator': validate_encoding_error_handler}),
-         ('Specify the language (as 2-letter code).  Default: en.',
+         ('Specify the language (as BCP 47 language tag).  Default: en.',
           ['--language', '-l'], {'dest': 'language_code', 'default': 'en',
                                  'metavar': '<name>'}),
          ('Write output file dependencies to <file>.',
         raise KeyError('No option with dest == %r.' % dest)
 
 
-class ConfigParser(CP.ConfigParser):
+class ConfigParser(CP.RawConfigParser):
 
     old_settings = {
         'pep_stylesheet': ('pep_html writer', 'stylesheet'),
 """
 
     def __init__(self, *args, **kwargs):
-        CP.ConfigParser.__init__(self, *args, **kwargs)
+        CP.RawConfigParser.__init__(self, *args, **kwargs)
 
         self._files = []
         """List of paths of configuration files read."""
 
+        self._stderr = ErrorOutput()
+        """Wrapper around sys.stderr catching en-/decoding errors"""
+
     def read(self, filenames, option_parser):
         if type(filenames) in (str, unicode):
             filenames = [filenames]
             except IOError:
                 continue
             try:
-                CP.ConfigParser.readfp(self, fp, filename)
+                if sys.version_info < (3,2):
+                    CP.RawConfigParser.readfp(self, fp, filename)
+                else:
+                    CP.RawConfigParser.read_file(self, fp, filename)
             except UnicodeDecodeError:
-                sys.stderr.write(self.not_utf8_error % (filename, filename))
+                self._stderr.write(self.not_utf8_error % (filename, filename))
                 fp.close()
                 continue
             fp.close()
                 except KeyError:
                     continue
                 if option.validator:
-                    value = self.get(section, setting, raw=1)
+                    value = self.get(section, setting)
                     try:
                         new_value = option.validator(
                             setting, value, option_parser,
                     except Exception, error:
                         raise (ValueError(
                             'Error in config file "%s", section "[%s]":\n'
-                            '    %s: %s\n        %s = %s'
-                            % (filename, section, error.__class__.__name__,
-                               error, setting, value)), None, sys.exc_info()[2])
+                            '    %s\n'
+                            '        %s = %s'
+                            % (filename, section, ErrorString(error),
+                               setting, value)), None, sys.exc_info()[2])
                     self.set(section, setting, new_value)
                 if option.overrides:
                     self.set(section, option.overrides, None)
         section_dict = {}
         if self.has_section(section):
             for option in self.options(section):
-                section_dict[option] = self.get(section, option, raw=1)
+                section_dict[option] = self.get(section, option)
         return section_dict
 
 
-# $Id: io.py 6125 2009-09-11 14:24:35Z milde $
+# $Id: io.py 7073 2011-07-07 06:49:19Z milde $
 # Author: David Goodger <goodger@python.org>
 # Copyright: This module has been placed in the public domain.
 
 __docformat__ = 'reStructuredText'
 
 import sys
-try:
-    import locale
-except:
-    pass
 import re
+import codecs
 from docutils import TransformSpec
 from docutils._compat import b
-
+from docutils.error_reporting import locale_encoding, ErrorString, ErrorOutput
 
 class Input(TransformSpec):
 
                 # Apply heuristics only if no encoding is explicitly given and
                 # no BOM found.  Start with UTF-8, because that only matches
                 # data that *IS* UTF-8:
-                encodings = ['utf-8']
-                try:
-                    # for Python 2.2 compatibility
-                    encodings.append(locale.nl_langinfo(locale.CODESET))
-                except:
-                    pass
-                try:
-                    encodings.append(locale.getlocale()[1])
-                except:
-                    pass
-                try:
-                    encodings.append(locale.getdefaultlocale()[1])
-                except:
-                    pass
-                # fallback encoding:
-                encodings.append('latin-1')
-        error = None
-        error_details = ''
+                encodings = [enc for enc in ('utf-8',
+                                             locale_encoding, # can be None
+                                             'latin-1') # fallback encoding
+                             if enc]
         for enc in encodings:
-            if not enc:
-                continue
             try:
                 decoded = unicode(data, enc, self.error_handler)
                 self.successful_encoding = enc
                 # Return decoded, removing BOMs.
                 return decoded.replace(u'\ufeff', u'')
-            except (UnicodeError, LookupError), tmperror:
-                error = tmperror  # working around Python 3 deleting the
-                                  # error variable after the except clause
-        if error is not None:
-            error_details = '\n(%s: %s)' % (error.__class__.__name__, error)
+            except (UnicodeError, LookupError), err:
+                error = err # in Python 3, the <exception instance> is
+                            # local to the except clause
         raise UnicodeError(
             'Unable to decode input data.  Tried the following encodings: '
-            '%s.%s'
-            % (', '.join([repr(enc) for enc in encodings if enc]),
-               error_details))
+            '%s.\n(%s)' % (', '.join([repr(enc) for enc in encodings]),
+                         ErrorString(error)))
 
     coding_slug = re.compile(b("coding[:=]\s*([-\w.]+)"))
     """Encoding declaration pattern."""
 
-    byte_order_marks = ((b('\xef\xbb\xbf'), 'utf-8'),
-                        (b('\xfe\xff'), 'utf-16-be'),
-                        (b('\xff\xfe'), 'utf-16-le'),)
-    """Sequence of (start_bytes, encoding) tuples to for encoding detection.
+    byte_order_marks = ((codecs.BOM_UTF8, 'utf-8'), # actually 'utf-8-sig'
+                        (codecs.BOM_UTF16_BE, 'utf-16-be'),
+                        (codecs.BOM_UTF16_LE, 'utf-16-le'),)
+    """Sequence of (start_bytes, encoding) tuples for encoding detection.
     The first bytes of input data are checked against the start_bytes strings.
     A match indicates the given encoding."""
 
     """
     Input for single, simple file-like objects.
     """
-
     def __init__(self, source=None, source_path=None,
                  encoding=None, error_handler='strict',
-                 autoclose=1, handle_io_errors=1):
+                 autoclose=True, handle_io_errors=True, mode='rU'):
         """
         :Parameters:
             - `source`: either a file-like object (which is read directly), or
             - `source_path`: a path to a file, which is opened and then read.
             - `encoding`: the expected text encoding of the input file.
             - `error_handler`: the encoding error handler to use.
-            - `autoclose`: close automatically after read (boolean); always
-              false if `sys.stdin` is the source.
+            - `autoclose`: close automatically after read (except when
+              `sys.stdin` is the source).
             - `handle_io_errors`: summarize I/O errors here, and exit?
+            - `mode`: how the file is to be opened (see standard function
+              `open`). The default 'rU' provides universal newline support
+              for text files.
         """
         Input.__init__(self, source, source_path, encoding, error_handler)
         self.autoclose = autoclose
         self.handle_io_errors = handle_io_errors
+        self._stderr = ErrorOutput()
+
         if source is None:
             if source_path:
+                # Specify encoding in Python 3
+                if sys.version_info >= (3,0):
+                    kwargs = {'encoding': self.encoding,
+                              'errors': self.error_handler}
+                else:
+                    kwargs = {}
+
                 try:
-                    self.source = open(source_path, 'rb')
+                    self.source = open(source_path, mode, **kwargs)
                 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)
+                    print >>self._stderr, ErrorString(error)
+                    print >>self._stderr, (u'Unable to open source'
+                        u" file for reading ('%s'). Exiting." % source_path)
                     sys.exit(1)
             else:
                 self.source = sys.stdin
-                self.autoclose = None
         if not source_path:
             try:
                 self.source_path = self.source.name
         return [self.decode(line) for line in lines]
 
     def close(self):
-        self.source.close()
+        if self.source is not sys.stdin:
+            self.source.close()
 
 
 class FileOutput(Output):
     """
 
     def __init__(self, destination=None, destination_path=None,
-                 encoding=None, error_handler='strict', autoclose=1,
-                 handle_io_errors=1):
+                 encoding=None, error_handler='strict', autoclose=True,
+                 handle_io_errors=True):
         """
         :Parameters:
             - `destination`: either a file-like object (which is written
               `destination_path` given).
             - `destination_path`: a path to a file, which is opened and then
               written.
-            - `autoclose`: close automatically after write (boolean); always
-              false if `sys.stdout` is the destination.
+            - `autoclose`: close automatically after write (except when
+              `sys.stdout` or `sys.stderr` is the destination).
         """
         Output.__init__(self, destination, destination_path,
                         encoding, error_handler)
-        self.opened = 1
+        self.opened = True
         self.autoclose = autoclose
         self.handle_io_errors = handle_io_errors
+        self._stderr = ErrorOutput()
         if destination is None:
             if destination_path:
-                self.opened = None
+                self.opened = False
             else:
                 self.destination = sys.stdout
-                self.autoclose = None
         if not destination_path:
             try:
                 self.destination_path = self.destination.name
                 pass
 
     def open(self):
+        # Specify encoding in Python 3.
+        # (Do not use binary mode ('wb') as this prevents the
+        # conversion of newlines to the system specific default.)
+        if sys.version_info >= (3,0):
+            kwargs = {'encoding': self.encoding,
+                      'errors': self.error_handler}
+        else:
+            kwargs = {}
+
         try:
-            self.destination = open(self.destination_path, 'w')
+            self.destination = open(self.destination_path, 'w', **kwargs)
         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." % self.destination_path)
+            print >>self._stderr, ErrorString(error)
+            print >>self._stderr, (u'Unable to open destination file'
+                u" for writing ('%s').  Exiting." % self.destination_path)
             sys.exit(1)
-        self.opened = 1
+        self.opened = True
 
     def write(self, data):
-        """Encode `data`, write it to a single file, and return it."""
-        output = self.encode(data)
+        """Encode `data`, write it to a single file, and return it.
+
+        In Python 3, a (unicode) string is returned.
+        """
+        if sys.version_info >= (3,0):
+            output = data # in py3k, write expects a (Unicode) string
+        else:
+            output = self.encode(data)
         if not self.opened:
             self.open()
         try:
         return output
 
     def close(self):
-        self.destination.close()
-        self.opened = None
+        if self.destination not in (sys.stdout, sys.stderr):
+            self.destination.close()
+            self.opened = False
 
 
 class BinaryFileOutput(FileOutput):
         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." % self.destination_path)
+            print >>self._stderr, ErrorString(error)
+            print >>self._stderr, (u'Unable to open destination file'
+                u" for writing ('%s').  Exiting." % self.destination_path)
             sys.exit(1)
-        self.opened = 1
+        self.opened = True
 
 
 class StringInput(Input):

docutils/languages/__init__.py

-# $Id: __init__.py 5618 2008-07-28 08:37:32Z strank $
+# $Id: __init__.py 6433 2010-09-28 08:21:25Z milde $
 # Author: David Goodger <goodger@python.org>
 # Copyright: This module has been placed in the public domain.
 
 
 __docformat__ = 'reStructuredText'
 
+from docutils.utils import normalize_language_tag
+
 _languages = {}
 
-def get_language(language_code):
-    if language_code in _languages:
-        return _languages[language_code]
-    module = __import__(language_code, globals(), locals())
-    _languages[language_code] = module
+def get_language(language_code, reporter):
+    """Return module with language localizations.
+
+    `language_code` is a "BCP 47" language tag.
+    If there is no matching module, warn and fall back to English.
+    """
+    # TODO: use a dummy module returning emtpy strings?, configurable?
+    for tag in normalize_language_tag(language_code):
+        if tag in _languages:
+            return _languages[tag]
+        try:
+            module = __import__(tag, globals(), locals())
+        except ImportError:
+            continue
+        _languages[tag] = module
+        return module
+    reporter.warning(
+        'language "%s" not supported: ' % language_code +
+        'Docutils-generated text will be in English.')
+    module = __import__('en', globals(), locals())
+    _languages[tag] = module # warn only one time!
     return module

docutils/languages/lt.py

+# -*- coding: utf8 -*-
+# $Id: lt.py 6459 2010-10-29 22:07:34Z milde $
+# Author: Dalius Dobravolskas <dalius.do...@gmail.com>
+# Copyright: This module has been placed in the public domain.
+
+# New language mappings are welcome.  Before doing a new translation, please
+# read <http://docutils.sf.net/docs/howto/i18n.html>.  Two files must be
+# translated for each language: one in docutils/languages, the other in
+# docutils/parsers/rst/languages.
+
+"""
+English-language mappings for language-dependent features of Docutils.
+"""
+
+__docformat__ = 'reStructuredText'
+
+labels = {
+      # fixed: language-dependent
+      'author': 'Autorius',
+      'authors': 'Autoriai',
+      'organization': 'Organizacija',
+      'address': 'Adresas',
+      'contact': 'Kontaktas',
+      'version': 'Versija',
+      'revision': 'Revizija',
+      'status': u'Būsena',
+      'date': 'Data',
+      'copyright': u'Autoriaus teisės',
+      'dedication': 'Dedikacija',
+      'abstract': 'Santrauka',
+      'attention': u'Dėmesio!',
+      'caution': 'Atsargiai!',
+      'danger': '!PAVOJINGA!',
+      'error': 'Klaida',
+      'hint': u'Užuomina',
+      'important': 'Svarbu',
+      'note': 'Pastaba',
+      'tip': 'Patarimas',
+      'warning': u'Įspėjimas',
+      'contents': 'Turinys'}
+"""Mapping of node class name to label text."""
+
+bibliographic_fields = {
+      # language-dependent: fixed
+      'autorius': 'author',
+      'autoriai': 'authors',
+      'organizacija': 'organization',
+      'adresas': 'address',
+      'kontaktas': 'contact',
+      'versija': 'version',
+      'revizija': 'revision',
+      'būsena': 'status',
+      'data': 'date',
+      'autoriaus teisės': 'copyright',
+      'dedikacija': 'dedication',
+      'santrauka': 'abstract'}
+"""English (lowcased) to canonical name mapping for bibliographic fields."""
+
+author_separators = [';', ',']
+"""List of separator strings for the 'Authors' bibliographic field. Tried in
+order."""
+# -*- coding: utf8 -*-
+# $Id: lt.py 6459 2010-10-29 22:07:34Z milde $
+# Author: David Goodger <goodger@python.org>
+# Copyright: This module has been placed in the public domain.
+
+# New language mappings are welcome.  Before doing a new translation, please
+# read <http://docutils.sf.net/docs/howto/i18n.html>.  Two files must be
+# translated for each language: one in docutils/languages, the other in
+# docutils/parsers/rst/languages.
+
+"""
+English-language mappings for language-dependent features of Docutils.
+"""
+
+__docformat__ = 'reStructuredText'
+
+labels = {
+      # fixed: language-dependent
+      'author': 'Autorius',
+      'authors': 'Autoriai',
+      'organization': 'Organizacija',
+      'address': 'Adresas',
+      'contact': 'Kontaktas',
+      'version': 'Versija',
+      'revision': 'Revizija',
+      'status': u'Būsena',
+      'date': 'Data',
+      'copyright': u'Autoriaus teisės',
+      'dedication': 'Dedikacija',
+      'abstract': 'Santrauka',
+      'attention': u'Dėmesio!',
+      'caution': 'Atsargiai!',
+      'danger': '!PAVOJINGA!',
+      'error': 'Klaida',
+      'hint': u'Užuomina',
+      'important': 'Svarbu',
+      'note': 'Pastaba',
+      'tip': 'Patarimas',
+      'warning': u'Įspėjimas',
+      'contents': 'Turinys'}
+"""Mapping of node class name to label text."""
+
+bibliographic_fields = {
+      # language-dependent: fixed
+      'autorius': 'author',
+      'autoriai': 'authors',
+      'organizacija': 'organization',
+      'adresas': 'address',
+      'kontaktas': 'contact',
+      'versija': 'version',
+      'revizija': 'revision',
+      'būsena': 'status',
+      'data': 'date',
+      'autoriaus teisės': 'copyright',
+      'dedikacija': 'dedication',
+      'santrauka': 'abstract'}
+"""English (lowcased) to canonical name mapping for bibliographic fields."""
+
+author_separators = [';', ',']
+"""List of separator strings for the 'Authors' bibliographic field. Tried in
+order."""

docutils/math/__init__.py

+# :Id: $Id: __init__.py 7059 2011-06-27 20:54:13Z milde $
+# :Author: Guenter Milde.
+# :License: Released under the terms of the `2-Clause BSD license`_, in short:
+#
+#    Copying and distribution of this file, with or without modification,
+#    are permitted in any medium without royalty provided the copyright
+#    notice and this notice are preserved.
+#    This file is offered as-is, without any warranty.
+#
+# .. _2-Clause BSD license: http://www.spdx.org/licenses/BSD-2-Clause
+
+"""
+This is the Docutils (Python Documentation Utilities) "math" sub-package.
+
+It contains various modules for conversion between different math formats
+(LaTeX, MathML, HTML).
+
+:math2html:    LaTeX math -> HTML conversion from eLyXer
+:latex2mathml: LaTeX math -> presentational MathML
+:unimathsymbols2tex: Unicode symbol to LaTeX math translation table
+"""
+__all__ = ['math2html',
+           'latex2mathml',
+           'unimathsymbols2tex']
+
+# helpers for Docutils math support
+# =================================
+
+def pick_math_environment(code, numbered=False):
+    """Return the right math environment to display `code`.
+
+    The test simply looks for line-breaks (``\\``) outside environments.
+    Multi-line formulae are set with ``align``, one-liners with
+    ``equation``.
+
+    If `numbered` evaluates to ``False``, the "starred" versions are used
+    to suppress numbering.
+    """
+    # cut out environment content:
+    chunks = code.split(r'\begin{')
+    toplevel_code = ''.join([chunk.split(r'\end{')[-1]
+                             for chunk in chunks])
+    if toplevel_code.find(r'\\') >= 0:
+        env = 'align'
+    else:
+        env = 'equation'
+    if not numbered:
+        env += '*'
+    return env

docutils/math/latex2mathml.py

+#!/usr/bin/env python
+# -*- coding: utf8 -*-
+
+# :Id: $Id: latex2mathml.py 7058 2011-06-27 11:38:56Z milde $
+# :Copyright: © 2010 Günter Milde.
+#             Based on rst2mathml.py from the latex_math sandbox project
+#             © 2005 Jens Jørgen Mortensen
+# :License: Released under the terms of the `2-Clause BSD license`_, in short:
+# 
+#    Copying and distribution of this file, with or without modification,
+#    are permitted in any medium without royalty provided the copyright
+#    notice and this notice are preserved.
+#    This file is offered as-is, without any warranty.
+# 
+# .. _2-Clause BSD license: http://www.spdx.org/licenses/BSD-2-Clause
+
+
+"""Convert LaTex math code into presentational MathML"""
+
+# Based on the `latex_math` sandbox project by Jens Jørgen Mortensen
+
+
+# LaTeX to MathML translation stuff:
+class math:
+    """Base class for MathML elements."""
+
+    nchildren = 1000000
+    """Required number of children"""
+
+    def __init__(self, children=None, inline=None):
+        """math([children]) -> MathML element
+
+        children can be one child or a list of children."""
+
+        self.children = []
+        if children is not None:
+            if type(children) is list:
+                for child in children:
+                    self.append(child)
+            else:
+                # Only one child:
+                self.append(children)
+
+        if inline is not None:
+            self.inline = inline
+
+    def __repr__(self):
+        if hasattr(self, 'children'):
+            return self.__class__.__name__ + '(%s)' % \
+                   ','.join([repr(child) for child in self.children])
+        else:
+            return self.__class__.__name__
+
+    def full(self):
+        """Room for more children?"""
+
+        return len(self.children) >= self.nchildren
+
+    def append(self, child):
+        """append(child) -> element
+
+        Appends child and returns self if self is not full or first
+        non-full parent."""
+
+        assert not self.full()
+        self.children.append(child)
+        child.parent = self
+        node = self
+        while node.full():
+            node = node.parent
+        return node
+
+    def delete_child(self):
+        """delete_child() -> child
+
+        Delete last child and return it."""
+
+        child = self.children[-1]
+        del self.children[-1]
+        return child
+
+    def close(self):
+        """close() -> parent
+
+        Close element and return first non-full element."""
+
+        parent = self.parent
+        while parent.full():
+            parent = parent.parent
+        return parent
+
+    def xml(self):
+        """xml() -> xml-string"""
+
+        return self.xml_start() + self.xml_body() + self.xml_end()
+
+    def xml_start(self):
+        if not hasattr(self, 'inline'):
+            return ['<%s>' % self.__class__.__name__]
+        xmlns = 'http://www.w3.org/1998/Math/MathML'
+        if self.inline:
+            return ['<math xmlns="%s">' % xmlns]
+        else:
+            return ['<math xmlns="%s" mode="display">' % xmlns]
+
+    def xml_end(self):
+        return ['</%s>' % self.__class__.__name__]
+
+    def xml_body(self):
+        xml = []
+        for child in self.children:
+            xml.extend(child.xml())
+        return xml
+
+class mrow(math):
+    def xml_start(self):
+        return ['\n<%s>' % self.__class__.__name__]
+
+class mtable(math):
+    def xml_start(self):
+        return ['\n<%s>' % self.__class__.__name__]
+
+class mtr(mrow): pass
+class mtd(mrow): pass
+
+class mx(math):
+    """Base class for mo, mi, and mn"""
+
+    nchildren = 0
+    def __init__(self, data):
+        self.data = data
+
+    def xml_body(self):
+        return [self.data]
+
+class mo(mx):
+    translation = {'<': '&lt;', '>': '&gt;'}
+    def xml_body(self):
+        return [self.translation.get(self.data, self.data)]
+
+class mi(mx): pass
+class mn(mx): pass
+
+class msub(math):
+    nchildren = 2
+
+class msup(math):
+    nchildren = 2
+
+class msqrt(math):
+    nchildren = 1
+
+class mroot(math):
+    nchildren = 2
+
+class mfrac(math):
+    nchildren = 2
+
+class msubsup(math):
+    nchildren = 3
+    def __init__(self, children=None, reversed=False):
+        self.reversed = reversed
+        math.__init__(self, children)
+
+    def xml(self):
+        if self.reversed:
+##            self.children[1:3] = self.children[2:0:-1]
+            self.children[1:3] = [self.children[2], self.children[1]]
+            self.reversed = False
+        return math.xml(self)
+
+class mfenced(math):
+    translation = {'\\{': '{', '\\langle': u'\u2329',
+                   '\\}': '}', '\\rangle': u'\u232A',
+                   '.': ''}
+    def __init__(self, par):
+        self.openpar = par
+        math.__init__(self)
+
+    def xml_start(self):
+        open = self.translation.get(self.openpar, self.openpar)
+        close = self.translation.get(self.closepar, self.closepar)
+        return ['<mfenced open="%s" close="%s">' % (open, close)]
+
+class mspace(math):
+    nchildren = 0
+
+class mstyle(math):
+    def __init__(self, children=None, nchildren=None, **kwargs):
+        if nchildren is not None:
+            self.nchildren = nchildren
+        math.__init__(self, children)
+        self.attrs = kwargs
+
+    def xml_start(self):
+        return ['<mstyle '] + ['%s="%s"' % item
+                               for item in self.attrs.items()] + ['>']
+
+class mover(math):
+    nchildren = 2
+    def __init__(self, children=None, reversed=False):
+        self.reversed = reversed
+        math.__init__(self, children)
+
+    def xml(self):
+        if self.reversed:
+            self.children.reverse()
+            self.reversed = False
+        return math.xml(self)
+
+class munder(math):
+    nchildren = 2
+
+class munderover(math):
+    nchildren = 3
+    def __init__(self, children=None):
+        math.__init__(self, children)
+
+class mtext(math):
+    nchildren = 0
+    def __init__(self, text):
+        self.text = text
+
+    def xml_body(self):
+        return [self.text]
+
+#        TeX      spacing    combining
+over = {'acute': u'\u00B4',  # u'\u0301',
+        'bar':   u'\u00AF',  # u'\u0304',
+        'breve': u'\u02D8',  # u'\u0306',
+        'check': u'\u02C7',  # u'\u030C',
+        'dot':   u'\u02D9',  # u'\u0307',
+        'ddot':  u'\u00A8',  # u'\u0308',
+        'dddot':             u'\u20DB',
+        'grave': u'`',       # u'\u0300',
+        'hat':   u'^',       # u'\u0302',
+        'tilde': u'\u02DC',  # u'\u0303',
+        # 'overline':        # u'\u0305',
+        'vec':               u'\u20D7'}
+
+Greek = { # Upper case greek letters:
+    'Phi':u'\u03a6', 'Xi':u'\u039e', 'Sigma':u'\u03a3',
+    'Psi':u'\u03a8', 'Delta':u'\u0394', 'Theta':u'\u0398',
+    'Upsilon':u'\u03d2', 'Pi':u'\u03a0', 'Omega':u'\u03a9',
+    'Gamma':u'\u0393', 'Lambda':u'\u039b'}
+
+letters = { # Lower case greek letters (and dotless i, j):
+    # 'imath':u'i', 'jmath':u'i', # when used with combining accents
+    'imath':u'\u0131', 'jmath':u'\u0237',
+    'tau':u'\u03c4', 'phi':u'\u03d5', 'xi':u'\u03be', 'iota':u'\u03b9',
+    'epsilon':u'\u03f5', 'varrho':u'\u03f1', 'varsigma':u'\u03c2',
+    'beta':u'\u03b2', 'psi':u'\u03c8', 'rho':u'\u03c1',
+    'delta':u'\u03b4', 'alpha':u'\u03b1', 'zeta':u'\u03b6',
+    'omega':u'\u03c9', 'varepsilon':u'\u03b5', 'kappa':u'\u03ba',
+    'vartheta':u'\u03d1', 'chi':u'\u03c7', 'upsilon':u'\u03c5',
+    'sigma':u'\u03c3', 'varphi':u'\u03c6', 'varpi':u'\u03d6',
+    'mu':u'\u03bc', 'eta':u'\u03b7', 'theta':u'\u03b8', 'pi':u'\u03c0',
+    'varkappa':u'\u03f0', 'nu':u'\u03bd', 'gamma':u'\u03b3',
+    'lambda':u'\u03bb'}
+
+special = {
+    # Binary operation symbols:
+    'wedge':u'\u2227', 'diamond':u'\u22c4', 'star':u'\u22c6',
+    'amalg':u'\u2a3f', 'ast':u'\u2217', 'odot':u'\u2299',
+    'triangleleft':u'\u25c1', 'bigtriangleup':u'\u25b3',
+    'ominus':u'\u2296', 'ddagger':u'\u2021', 'wr':u'\u2240',
+    'otimes':u'\u2297', 'sqcup':u'\u2294', 'oplus':u'\u2295',
+    'bigcirc':u'\u25cb', 'oslash':u'\u2298', 'sqcap':u'\u2293',
+    'bullet':u'\u2219', 'cup':u'\u222a', 'cdot':u'\u22c5',
+    'cap':u'\u2229', 'bigtriangledown':u'\u25bd', 'times':u'\xd7',
+    'setminus':u'\u2216', 'circ':u'\u2218', 'vee':u'\u2228',
+    'uplus':u'\u228e', 'mp':u'\u2213', 'dagger':u'\u2020',
+    'triangleright':u'\u25b7', 'div':u'\xf7', 'pm':u'\xb1',
+    # Relation symbols:
+    'subset':u'\u2282', 'propto':u'\u221d', 'geq':u'\u2265',
+    'ge':u'\u2265', 'sqsubset':u'\u228f', 'Join':u'\u2a1d',
+    'frown':u'\u2322', 'models':u'\u22a7', 'supset':u'\u2283',
+    'in':u'\u2208', 'doteq':u'\u2250', 'dashv':u'\u22a3',
+    'gg':u'\u226b', 'leq':u'\u2264', 'succ':u'\u227b',
+    'vdash':u'\u22a2', 'cong':u'\u2245', 'simeq':u'\u2243',
+    'subseteq':u'\u2286', 'parallel':u'\u2225', 'equiv':u'\u2261',
+    'ni':u'\u220b', 'le':u'\u2264', 'approx':u'\u2248',
+    'precsim':u'\u227e', 'sqsupset':u'\u2290', 'll':u'\u226a',
+    'sqsupseteq':u'\u2292', 'mid':u'\u2223', 'prec':u'\u227a',
+    'succsim':u'\u227f', 'bowtie':u'\u22c8', 'perp':u'\u27c2',
+    'sqsubseteq':u'\u2291', 'asymp':u'\u224d', 'smile':u'\u2323',
+    'supseteq':u'\u2287', 'sim':u'\u223c', 'neq':u'\u2260',
+    # Arrow symbols:
+    'searrow':u'\u2198', 'updownarrow':u'\u2195', 'Uparrow':u'\u21d1',
+    'longleftrightarrow':u'\u27f7', 'Leftarrow':u'\u21d0',
+    'longmapsto':u'\u27fc', 'Longleftarrow':u'\u27f8',
+    'nearrow':u'\u2197', 'hookleftarrow':u'\u21a9',
+    'downarrow':u'\u2193', 'Leftrightarrow':u'\u21d4',
+    'longrightarrow':u'\u27f6', 'rightharpoondown':u'\u21c1',
+    'longleftarrow':u'\u27f5', 'rightarrow':u'\u2192',
+    'Updownarrow':u'\u21d5', 'rightharpoonup':u'\u21c0',
+    'Longleftrightarrow':u'\u27fa', 'leftarrow':u'\u2190',
+    'mapsto':u'\u21a6', 'nwarrow':u'\u2196', 'uparrow':u'\u2191',
+    'leftharpoonup':u'\u21bc', 'leftharpoondown':u'\u21bd',
+    'Downarrow':u'\u21d3', 'leftrightarrow':u'\u2194',
+    'Longrightarrow':u'\u27f9', 'swarrow':u'\u2199',
+    'hookrightarrow':u'\u21aa', 'Rightarrow':u'\u21d2',
+    'to':u'\u2192',
+    # Miscellaneous symbols:
+    'infty':u'\u221e', 'surd':u'\u221a',
+    'partial':u'\u2202', 'ddots':u'\u22f1', 'exists':u'\u2203',
+    'flat':u'\u266d', 'diamondsuit':u'\u2662', 'wp':u'\u2118',
+    'spadesuit':u'\u2660', 'Re':u'\u211c', 'vdots':u'\u22ee',
+    'aleph':u'\u2135', 'clubsuit':u'\u2663', 'sharp':u'\u266f',
+    'angle':u'\u2220', 'prime':u'\u2032', 'natural':u'\u266e',
+    'ell':u'\u2113', 'neg':u'\xac', 'top':u'\u22a4', 'nabla':u'\u2207',
+    'bot':u'\u22a5', 'heartsuit':u'\u2661', 'cdots':u'\u22ef',
+    'Im':u'\u2111', 'forall':u'\u2200',
+    'hbar':u'\u210f', 'emptyset':u'\u2205',
+    # Variable-sized symbols:
+    'bigotimes':u'\u2a02', 'coprod':u'\u2210', 'int':u'\u222b',
+    'sum':u'\u2211', 'bigodot':u'\u2a00', 'bigcup':u'\u22c3',
+    'biguplus':u'\u2a04', 'bigcap':u'\u22c2', 'bigoplus':u'\u2a01',
+    'oint':u'\u222e', 'bigvee':u'\u22c1', 'bigwedge':u'\u22c0',
+    'prod':u'\u220f',
+    # Braces:
+    'langle':u'\u2329', 'rangle':u'\u232A'}
+
+sumintprod = ''.join([special[symbol] for symbol in
+                      ['sum', 'int', 'oint', 'prod']])
+
+functions = ['arccos', 'arcsin', 'arctan', 'arg', 'cos',  'cosh',
+             'cot',    'coth',   'csc',    'deg', 'det',  'dim',
+             'exp',    'gcd',    'hom',    'inf', 'ker',  'lg',
+             'lim',    'liminf', 'limsup', 'ln',  'log',  'max',
+             'min',    'Pr',     'sec',    'sin', 'sinh', 'sup',
+             'tan',    'tanh',
+             'injlim',  'varinjlim', 'varlimsup',
+             'projlim', 'varliminf', 'varprojlim']
+
+
+def parse_latex_math(string, inline=True):
+    """parse_latex_math(string [,inline]) -> MathML-tree
+
+    Returns a MathML-tree parsed from string.  inline=True is for
+    inline math and inline=False is for displayed math.
+
+    tree is the whole tree and node is the current element."""
+
+    # Normalize white-space:
+    string = ' '.join(string.split())
+
+    if inline:
+        node = mrow()
+        tree = math(node, inline=True)
+    else:
+        node = mtd()
+        tree = math(mtable(mtr(node)), inline=False)
+
+    while len(string) > 0:
+        n = len(string)
+        c = string[0]
+        skip = 1  # number of characters consumed
+        if n > 1:
+            c2 = string[1]
+        else:
+            c2 = ''
+##        print n, string, c, c2, node.__class__.__name__
+        if c == ' ':
+            pass
+        elif c == '\\':
+            if c2 in '{}':
+                node = node.append(mo(c2))
+                skip = 2
+            elif c2 == ' ':
+                node = node.append(mspace())
+                skip = 2
+            elif c2 == ',': # TODO: small space
+                node = node.append(mspace())
+                skip = 2
+            elif c2.isalpha():
+                # We have a LaTeX-name:
+                i = 2
+                while i < n and string[i].isalpha():
+                    i += 1
+                name = string[1:i]
+                node, skip = handle_keyword(name, node, string[i:])
+                skip += i
+            elif c2 == '\\':
+                # End of a row:
+                entry = mtd()
+                row = mtr(entry)
+                node.close().close().append(row)
+                node = entry
+                skip = 2
+            else:
+                raise SyntaxError(ur'Syntax error: "%s%s"' % (c, c2))
+        elif c.isalpha():
+            node = node.append(mi(c))
+        elif c.isdigit():
+            node = node.append(mn(c))
+        elif c in "+-*/=()[]|<>,.!?':;@":
+            node = node.append(mo(c))
+        elif c == '_':
+            child = node.delete_child()
+            if isinstance(child, msup):
+                sub = msubsup(child.children, reversed=True)
+            elif isinstance(child, mo) and child.data in sumintprod:
+                sub = munder(child)
+            else:
+                sub = msub(child)
+            node.append(sub)
+            node = sub
+        elif c == '^':
+            child = node.delete_child()
+            if isinstance(child, msub):
+                sup = msubsup(child.children)
+            elif isinstance(child, mo) and child.data in sumintprod:
+                sup = mover(child)
+            elif (isinstance(child, munder) and
+                  child.children[0].data in sumintprod):
+                sup = munderover(child.children)
+            else:
+                sup = msup(child)
+            node.append(sup)
+            node = sup
+        elif c == '{':
+            row = mrow()
+            node.append(row)
+            node = row
+        elif c == '}':
+            node = node.close()
+        elif c == '&':
+            entry = mtd()
+            node.close().append(entry)
+            node = entry
+        else:
+            raise SyntaxError(ur'Illegal character: "%s"' % c)
+        string = string[skip:]
+    return tree
+
+
+mathbb = {
+          'A': u'\U0001D538',
+          'B': u'\U0001D539',
+          'C': u'\u2102',
+          'D': u'\U0001D53B',
+          'E': u'\U0001D53C',
+          'F': u'\U0001D53D',
+          'G': u'\U0001D53E',
+          'H': u'\u210D',
+          'I': u'\U0001D540',
+          'J': u'\U0001D541',
+          'K': u'\U0001D542',
+          'L': u'\U0001D543',
+          'M': u'\U0001D544',
+          'N': u'\u2115',
+          'O': u'\U0001D546',
+          'P': u'\u2119',
+          'Q': u'\u211A',
+          'R': u'\u211D',
+          'S': u'\U0001D54A',
+          'T': u'\U0001D54B',
+          'U': u'\U0001D54C',
+          'V': u'\U0001D54D',
+          'W': u'\U0001D54E',
+          'X': u'\U0001D54F',
+          'Y': u'\U0001D550',
+          'Z': u'\u2124',
+         }
+
+mathscr = {
+           'A': u'\U0001D49C',
+           'B': u'\u212C',     # bernoulli function
+           'C': u'\U0001D49E',
+           'D': u'\U0001D49F',
+           'E': u'\u2130',
+           'F': u'\u2131',
+           'G': u'\U0001D4A2',
+           'H': u'\u210B',     # hamiltonian
+           'I': u'\u2110',
+           'J': u'\U0001D4A5',
+           'K': u'\U0001D4A6',
+           'L': u'\u2112',     # lagrangian
+           'M': u'\u2133',     # physics m-matrix
+           'N': u'\U0001D4A9',
+           'O': u'\U0001D4AA',
+           'P': u'\U0001D4AB',
+           'Q': u'\U0001D4AC',
+           'R': u'\u211B',
+           'S': u'\U0001D4AE',
+           'T': u'\U0001D4AF',
+           'U': u'\U0001D4B0',
+           'V': u'\U0001D4B1',
+           'W': u'\U0001D4B2',
+           'X': u'\U0001D4B3',
+           'Y': u'\U0001D4B4',
+           'Z': u'\U0001D4B5',
+           'a': u'\U0001D4B6',
+           'b': u'\U0001D4B7',
+           'c': u'\U0001D4B8',
+           'd': u'\U0001D4B9',
+           'e': u'\u212F',
+           'f': u'\U0001D4BB',
+           'g': u'\u210A',
+           'h': u'\U0001D4BD',
+           'i': u'\U0001D4BE',
+           'j': u'\U0001D4BF',
+           'k': u'\U0001D4C0',
+           'l': u'\U0001D4C1',
+           'm': u'\U0001D4C2',
+           'n': u'\U0001D4C3',
+           'o': u'\u2134',     # order of
+           'p': u'\U0001D4C5',
+           'q': u'\U0001D4C6',
+           'r': u'\U0001D4C7',
+           's': u'\U0001D4C8',
+           't': u'\U0001D4C9',
+           'u': u'\U0001D4CA',
+           'v': u'\U0001D4CB',
+           'w': u'\U0001D4CC',
+           'x': u'\U0001D4CD',
+           'y': u'\U0001D4CE',
+           'z': u'\U0001D4CF',
+          }
+
+negatables = {'=': u'\u2260',
+              '\in': u'\u2209',
+              '\equiv': u'\u2262'}
+
+
+def handle_keyword(name, node, string):
+    skip = 0
+    if len(string) > 0 and string[0] == ' ':
+        string = string[1:]
+        skip = 1
+    if name == 'begin':
+        if not string.startswith('{matrix}'):
+            raise SyntaxError(u'Environment not supported! '
+                              u'Supported environment: "matrix".')
+        skip += 8
+        entry = mtd()
+        table = mtable(mtr(entry))
+        node.append(table)
+        node = entry
+    elif name == 'end':
+        if not string.startswith('{matrix}'):
+            raise SyntaxError(ur'Expected "\end{matrix}"!')
+        skip += 8
+        node = node.close().close().close()
+    elif name in ('text', 'mathrm'):
+        if string[0] != '{':
+            raise SyntaxError(ur'Expected "\text{...}"!')
+        i = string.find('}')
+        if i == -1:
+            raise SyntaxError(ur'Expected "\text{...}"!')
+        node = node.append(mtext(string[1:i]))
+        skip += i + 1
+    elif name == 'sqrt':
+        sqrt = msqrt()
+        node.append(sqrt)
+        node = sqrt
+    elif name == 'frac':
+        frac = mfrac()
+        node.append(frac)
+        node = frac
+    elif name == 'left':
+        for par in ['(', '[', '|', '\\{', '\\langle', '.']:
+            if string.startswith(par):
+                break
+        else:
+            raise SyntaxError(u'Missing left-brace!')
+        fenced = mfenced(par)
+        node.append(fenced)
+        row = mrow()
+        fenced.append(row)
+        node = row
+        skip += len(par)
+    elif name == 'right':
+        for par in [')', ']', '|', '\\}', '\\rangle', '.']:
+            if string.startswith(par):
+                break
+        else:
+            raise SyntaxError(u'Missing right-brace!')
+        node = node.close()
+        node.closepar = par
+        node = node.close()
+        skip += len(par)
+    elif name == 'not':
+        for operator in negatables:
+            if string.startswith(operator):
+                break
+        else:
+            raise SyntaxError(ur'Expected something to negate: "\not ..."!')
+        node = node.append(mo(negatables[operator]))
+        skip += len(operator)
+    elif name == 'mathbf':
+        style = mstyle(nchildren=1, fontweight='bold')
+        node.append(style)
+        node = style
+    elif name == 'mathbb':
+        if string[0] != '{' or not string[1].isupper() or string[2] != '}':
+            raise SyntaxError(ur'Expected something like "\mathbb{A}"!')
+        node = node.append(mi(mathbb[string[1]]))
+        skip += 3
+    elif name in ('mathscr', 'mathcal'):
+        if string[0] != '{' or string[2] != '}':
+            raise SyntaxError(ur'Expected something like "\mathscr{A}"!')
+        node = node.append(mi(mathscr[string[1]]))
+        skip += 3
+    elif name == 'colon': # "normal" colon, not binary operator
+        node = node.append(mo(':')) # TODO: add ``lspace="0pt"``
+    elif name in letters:
+        node = node.append(mi(letters[name]))
+    elif name in Greek:
+        node = node.append(mo(Greek[name]))
+    elif name in special:
+        node = node.append(mo(special[name]))
+    elif name in functions:
+        node = node.append(mo(name))
+    else:
+        chr = over.get(name)
+        if chr is not None:
+            ovr = mover(mo(chr), reversed=True)
+            node.append(ovr)
+            node = ovr
+        else:
+            raise SyntaxError(u'Unknown LaTeX command: ' + name)
+
+    return node, skip

docutils/math/math2html.py

+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+
+#   math2html: convert LaTeX equations to HTML output.
+#
+#   Copyright (C) 2009-2011 Alex Fernández
+#
+#   Released under the terms of the `2-Clause BSD license'_, in short:
+#   Copying and distribution of this file, with or without modification,
+#   are permitted in any medium without royalty provided the copyright
+#   notice and this notice are preserved.
+#   This file is offered as-is, without any warranty.
+#
+# .. _2-Clause BSD license: http://www.spdx.org/licenses/BSD-2-Clause
+
+#   Based on eLyXer: convert LyX source files to HTML output.
+#   http://elyxer.nongnu.org/
+
+# --end--
+# Alex 20101110
+# eLyXer standalone formula conversion to HTML.
+
+
+
+
+import sys
+
+if sys.version_info < (2,4):
+  def reversed(sequence):
+    i = len(sequence)
+    while i > 0:
+        i = i - 1
+        yield sequence[i]
+
+
+class Trace(object):
+  "A tracing class"
+
+  debugmode = False
+  quietmode = False
+  showlinesmode = False
+
+  prefix = None
+
+  def debug(cls, message):
+    "Show a debug message"
+    if not Trace.debugmode or Trace.quietmode:
+      return
+    Trace.show(message, sys.stdout)
+
+  def message(cls, message):
+    "Show a trace message"
+    if Trace.quietmode:
+      return
+    if Trace.prefix and Trace.showlinesmode:
+      message = Trace.prefix + message
+    Trace.show(message, sys.stdout)
+
+  def error(cls, message):
+    "Show an error message"
+    message = '* ' + message
+    if Trace.prefix and Trace.showlinesmode:
+      message = Trace.prefix + message
+    Trace.show(message, sys.stderr)
+
+  def fatal(cls, message):
+    "Show an error message and terminate"
+    Trace.error('FATAL: ' + message)
+    exit(-1)
+
+  def show(cls, message, channel):
+    "Show a message out of a channel"
+    message = message.encode('utf-8')
+    channel.write(message + '\n')
+
+  debug = classmethod(debug)
+  message = classmethod(message)
+  error = classmethod(error)
+  fatal = classmethod(fatal)
+  show = classmethod(show)
+
+
+
+
+import os.path
+import sys
+
+
+class BibStylesConfig(object):
+  "Configuration class from elyxer.config file"
+
+  abbrvnat = {
+      
+      u'@article':u'$authors. $title. <i>$journal</i>,{ {$volume:}$pages,} $month $year.{ doi: $doi.}{ URL <a href="$url">$url</a>.}{ $note.}', 
+      u'cite':u'$surname($year)', 
+      u'default':u'$authors. <i>$title</i>. $publisher, $year.{ URL <a href="$url">$url</a>.}{ $note.}', 
+      }
+
+  alpha = {
+      
+      u'@article':u'$authors. $title.{ <i>$journal</i>{, {$volume}{($number)}}{: $pages}{, $year}.}{ <a href="$url">$url</a>.}{ <a href="$filename">$filename</a>.}{ $note.}', 
+      u'cite':u'$Sur$YY', 
+      u'default':u'$authors. $title.{ <i>$journal</i>,} $year.{ <a href="$url">$url</a>.}{ <a href="$filename">$filename</a>.}{ $note.}', 
+      }
+
+  authordate2 = {
+      
+      u'@article':u'$authors. $year. $title. <i>$journal</i>, <b>$volume</b>($number), $pages.{ URL <a href="$url">$url</a>.}{ $note.}', 
+      u'@book':u'$authors. $year. <i>$title</i>. $publisher.{ URL <a href="$url">$url</a>.}{ $note.}', 
+      u'cite':u'$surname, $year', 
+      u'default':u'$authors. $year. <i>$title</i>. $publisher.{ URL <a href="$url">$url</a>.}{ $note.}', 
+      }
+
+  default = {
+      
+      u'@article':u'$authors: “$title”, <i>$journal</i>,{ pp. $pages,} $year.{ URL <a href="$url">$url</a>.}{ $note.}', 
+      u'@book':u'{$authors: }<i>$title</i>{ ($editor, ed.)}.{{ $publisher,} $year.}{ URL <a href="$url">$url</a>.}{ $note.}', 
+      u'@booklet':u'$authors: <i>$title</i>.{{ $publisher,} $year.}{ URL <a href="$url">$url</a>.}{ $note.}', 
+      u'@conference':u'$authors: “$title”, <i>$journal</i>,{ pp. $pages,} $year.{ URL <a href="$url">$url</a>.}{ $note.}', 
+      u'@inbook':u'$authors: <i>$title</i>.{{ $publisher,} $year.}{ URL <a href="$url">$url</a>.}{ $note.}', 
+      u'@incollection':u'$authors: <i>$title</i>{ in <i>$booktitle</i>{ ($editor, ed.)}}.{{ $publisher,} $year.}{ URL <a href="$url">$url</a>.}{ $note.}', 
+      u'@inproceedings':u'$authors: “$title”, <i>$journal</i>,{ pp. $pages,} $year.{ URL <a href="$url">$url</a>.}{ $note.}', 
+      u'@manual':u'$authors: <i>$title</i>.{{ $publisher,} $year.}{ URL <a href="$url">$url</a>.}{ $note.}', 
+      u'@mastersthesis':u'$authors: <i>$title</i>.{{ $publisher,} $year.}{ URL <a href="$url">$url</a>.}{ $note.}', 
+      u'@misc':u'$authors: <i>$title</i>.{{ $publisher,}{ $howpublished,} $year.}{ URL <a href="$url">$url</a>.}{ $note.}', 
+      u'@phdthesis':u'$authors: <i>$title</i>.{{ $publisher,} $year.}{ URL <a href="$url">$url</a>.}{ $note.}', 
+      u'@proceedings':u'$authors: “$title”, <i>$journal</i>,{ pp. $pages,} $year.{ URL <a href="$url">$url</a>.}{ $note.}', 
+      u'@techreport':u'$authors: <i>$title</i>, $year.{ URL <a href="$url">$url</a>.}{ $note.}', 
+      u'@unpublished':u'$authors: “$title”, <i>$journal</i>, $year.{ URL <a href="$url">$url</a>.}{ $note.}', 
+      u'cite':u'$index', 
+      u'default':u'$authors: <i>$title</i>.{{ $publisher,} $year.}{ URL <a href="$url">$url</a>.}{ $note.}', 
+      }
+
+  defaulttags = {
+      u'YY':u'??', u'authors':u'', u'surname':u'', 
+      }
+
+  ieeetr = {
+      
+      u'@article':u'$authors, “$title”, <i>$journal</i>, vol. $volume, no. $number, pp. $pages, $year.{ URL <a href="$url">$url</a>.}{ $note.}', 
+      u'@book':u'$authors, <i>$title</i>. $publisher, $year.{ URL <a href="$url">$url</a>.}{ $note.}', 
+      u'cite':u'$index', 
+      u'default':u'$authors, “$title”. $year.{ URL <a href="$url">$url</a>.}{ $note.}', 
+      }
+
+  plain = {
+      
+      u'@article':u'$authors. $title.{ <i>$journal</i>{, {$volume}{($number)}}{:$pages}{, $year}.}{ URL <a href="$url">$url</a>.}{ $note.}', 
+      u'@book':u'$authors. <i>$title</i>. $publisher,{ $month} $year.{ URL <a href="$url">$url</a>.}{ $note.}', 
+      u'@incollection':u'$authors. $title.{ In <i>$booktitle</i> {($editor, ed.)}.} $publisher,{ $month} $year.{ URL <a href="$url">$url</a>.}{ $note.}', 
+      u'@inproceedings':u'$authors. $title. { <i>$booktitle</i>{, {$volume}{($number)}}{:$pages}{, $year}.}{ URL <a href="$url">$url</a>.}{ $note.}', 
+      u'cite':u'$index', 
+      u'default':u'{$authors. }$title.{{ $publisher,} $year.}{ URL <a href="$url">$url</a>.}{ $note.}', 
+      }
+
+  vancouver = {
+      
+      u'@article':u'$authors. $title. <i>$journal</i>, $year{;{<b>$volume</b>}{($number)}{:$pages}}.{ URL: <a href="$url">$url</a>.}{ $note.}', 
+      u'@book':u'$authors. $title. {$publisher, }$year.{ URL: <a href="$url">$url</a>.}{ $note.}', 
+      u'cite':u'$index', 
+      u'default':u'$authors. $title; {$publisher, }$year.{ $howpublished.}{ URL: <a href="$url">$url</a>.}{ $note.}', 
+      }
+
+class BibTeXConfig(object):
+  "Configuration class from elyxer.config file"
+
+  replaced = {
+      u'--':u'—', u'..':u'.', 
+      }
+
+class ContainerConfig(object):
+  "Configuration class from elyxer.config file"
+
+  endings = {
+      u'Align':u'\\end_layout', u'BarredText':u'\\bar', 
+      u'BoldText':u'\\series', u'Cell':u'</cell', 
+      u'ChangeDeleted':u'\\change_unchanged', 
+      u'ChangeInserted':u'\\change_unchanged', u'ColorText':u'\\color', 
+      u'EmphaticText':u'\\emph', u'Hfill':u'\\hfill', u'Inset':u'\\end_inset', 
+      u'Layout':u'\\end_layout', u'LyXFooter':u'\\end_document', 
+      u'LyXHeader':u'\\end_header', u'Row':u'</row', u'ShapedText':u'\\shape', 
+      u'SizeText':u'\\size', u'StrikeOut':u'\\strikeout', 
+      u'TextFamily':u'\\family', u'VersalitasText':u'\\noun', 
+      }
+
+  extracttext = {
+      u'allowed':[u'StringContainer',u'Constant',u'FormulaConstant',], 
+      u'cloned':[u'',], 
+      u'extracted':[u'PlainLayout',u'TaggedText',u'Align',u'Caption',u'TextFamily',u'EmphaticText',u'VersalitasText',u'BarredText',u'SizeText',u'ColorText',u'LangLine',u'Formula',u'Bracket',u'RawText',u'BibTag',u'FormulaNumber',u'AlphaCommand',u'EmptyCommand',u'OneParamFunction',u'SymbolFunction',u'TextFunction',u'FontFunction',u'CombiningFunction',u'DecoratingFunction',u'FormulaSymbol',u'BracketCommand',u'TeXCode',], 
+      }
+
+  startendings = {
+      u'\\begin_deeper':u'\\end_deeper', u'\\begin_inset':u'\\end_inset', 
+      u'\\begin_layout':u'\\end_layout', 
+      }
+
+  starts = {
+      u'':u'StringContainer', u'#LyX':u'BlackBox', u'</lyxtabular':u'BlackBox', 
+      u'<cell':u'Cell', u'<column':u'Column', u'<row':u'Row', 
+      u'\\align':u'Align', u'\\bar':u'BarredText', 
+      u'\\bar default':u'BlackBox', u'\\bar no':u'BlackBox', 
+      u'\\begin_body':u'BlackBox', u'\\begin_deeper':u'DeeperList', 
+      u'\\begin_document':u'BlackBox', u'\\begin_header':u'LyXHeader', 
+      u'\\begin_inset Argument':u'ShortTitle', 
+      u'\\begin_inset Box':u'BoxInset', u'\\begin_inset Branch':u'Branch', 
+      u'\\begin_inset Caption':u'Caption', 
+      u'\\begin_inset CommandInset bibitem':u'BiblioEntry', 
+      u'\\begin_inset CommandInset bibtex':u'BibTeX', 
+      u'\\begin_inset CommandInset citation':u'BiblioCitation', 
+      u'\\begin_inset CommandInset href':u'URL', 
+      u'\\begin_inset CommandInset include':u'IncludeInset', 
+      u'\\begin_inset CommandInset index_print':u'PrintIndex', 
+      u'\\begin_inset CommandInset label':u'Label', 
+      u'\\begin_inset CommandInset line':u'LineInset', 
+      u'\\begin_inset CommandInset nomencl_print':u'PrintNomenclature', 
+      u'\\begin_inset CommandInset nomenclature':u'NomenclatureEntry', 
+      u'\\begin_inset CommandInset ref':u'Reference', 
+      u'\\begin_inset CommandInset toc':u'TableOfContents', 
+      u'\\begin_inset ERT':u'ERT', u'\\begin_inset Flex':u'FlexInset', 
+      u'\\begin_inset Flex Chunkref':u'NewfangledChunkRef', 
+      u'\\begin_inset Flex Marginnote':u'SideNote', 
+      u'\\begin_inset Flex Sidenote':u'SideNote', 
+      u'\\begin_inset Flex URL':u'FlexURL', u'\\begin_inset Float':u'Float', 
+      u'\\begin_inset FloatList':u'ListOf', u'\\begin_inset Foot':u'Footnote', 
+      u'\\begin_inset Formula':u'Formula', 
+      u'\\begin_inset FormulaMacro':u'FormulaMacro', 
+      u'\\begin_inset Graphics':u'Image', 
+      u'\\begin_inset Index':u'IndexReference', 
+      u'\\begin_inset Info':u'InfoInset', 
+      u'\\begin_inset LatexCommand bibitem':u'BiblioEntry', 
+      u'\\begin_inset LatexCommand bibtex':u'BibTeX', 
+      u'\\begin_inset LatexCommand cite':u'BiblioCitation', 
+      u'\\begin_inset LatexCommand citealt':u'BiblioCitation', 
+      u'\\begin_inset LatexCommand citep':u'BiblioCitation', 
+      u'\\begin_inset LatexCommand citet':u'BiblioCitation', 
+      u'\\begin_inset LatexCommand htmlurl':u'URL', 
+      u'\\begin_inset LatexCommand index':u'IndexReference', 
+      u'\\begin_inset LatexCommand label':u'Label', 
+      u'\\begin_inset LatexCommand nomenclature':u'NomenclatureEntry', 
+      u'\\begin_inset LatexCommand prettyref':u'Reference', 
+      u'\\begin_inset LatexCommand printindex':u'PrintIndex', 
+      u'\\begin_inset LatexCommand printnomenclature':u'PrintNomenclature', 
+      u'\\begin_inset LatexCommand ref':u'Reference', 
+      u'\\begin_inset LatexCommand tableofcontents':u'TableOfContents', 
+      u'\\begin_inset LatexCommand url':u'URL', 
+      u'\\begin_inset LatexCommand vref':u'Reference', 
+      u'\\begin_inset Marginal':u'SideNote', 
+      u'\\begin_inset Newline':u'NewlineInset', 
+      u'\\begin_inset Newpage':u'NewPageInset', u'\\begin_inset Note':u'Note', 
+      u'\\begin_inset OptArg':u'ShortTitle', 
+      u'\\begin_inset Phantom':u'PhantomText', 
+      u'\\begin_inset Quotes':u'QuoteContainer', 
+      u'\\begin_inset Tabular':u'Table', u'\\begin_inset Text':u'InsetText', 
+      u'\\begin_inset VSpace':u'VerticalSpace', u'\\begin_inset Wrap':u'Wrap', 
+      u'\\begin_inset listings':u'Listing', u'\\begin_inset space':u'Space', 
+      u'\\begin_layout':u'Layout', u'\\begin_layout Abstract':u'Abstract', 
+      u'\\begin_layout Author':u'Author', 
+      u'\\begin_layout Bibliography':u'Bibliography', 
+      u'\\begin_layout Chunk':u'NewfangledChunk', 
+      u'\\begin_layout Description':u'Description', 
+      u'\\begin_layout Enumerate':u'ListItem', 
+      u'\\begin_layout Itemize':u'ListItem', u'\\begin_layout List':u'List', 
+      u'\\begin_layout LyX-Code':u'LyXCode', 
+      u'\\begin_layout Plain':u'PlainLayout', 
+      u'\\begin_layout Standard':u'StandardLayout', 
+      u'\\begin_layout Title':u'Title', u'\\begin_preamble':u'LyXPreamble', 
+      u'\\change_deleted':u'ChangeDeleted', 
+      u'\\change_inserted':u'ChangeInserted', 
+      u'\\change_unchanged':u'BlackBox', u'\\color':u'ColorText', 
+      u'\\color inherit':u'BlackBox', u'\\color none':u'BlackBox', 
+      u'\\emph default':u'BlackBox', u'\\emph off':u'BlackBox', 
+      u'\\emph on':u'EmphaticText', u'\\emph toggle':u'EmphaticText', 
+      u'\\end_body':u'LyXFooter', u'\\family':u'TextFamily', 
+      u'\\family default':u'BlackBox', u'\\family roman':u'BlackBox', 
+      u'\\hfill':u'Hfill', u'\\labelwidthstring':u'BlackBox', 
+      u'\\lang':u'LangLine', u'\\length':u'InsetLength', 
+      u'\\lyxformat':u'LyXFormat', u'\\lyxline':u'LyXLine', 
+      u'\\newline':u'Newline', u'\\newpage':u'NewPage', 
+      u'\\noindent':u'BlackBox', u'\\noun default':u'BlackBox', 
+      u'\\noun off':u'BlackBox', u'\\noun on':u'VersalitasText', 
+      u'\\paragraph_spacing':u'BlackBox', u'\\series bold':u'BoldText', 
+      u'\\series default':u'BlackBox', u'\\series medium':u'BlackBox', 
+      u'\\shape':u'ShapedText', u'\\shape default':u'BlackBox', 
+      u'\\shape up':u'BlackBox', u'\\size':u'SizeText', 
+      u'\\size normal':u'BlackBox', u'\\start_of_appendix':u'StartAppendix', 
+      u'\\strikeout default':u'BlackBox', u'\\strikeout on':u'StrikeOut', 
+      }
+
+  string = {
+      u'startcommand':u'\\', 
+      }
+
+  table = {
+      u'headers':[u'<lyxtabular',u'<features',], 
+      }
+
+class EscapeConfig(object):
+  "Configuration class from elyxer.config file"
+
+  chars = {
+      u'\n':u'', u' -- ':u' — ', u'\'':u'’', u'---':u'—', u'`':u'‘', 
+      }
+
+  commands = {
+      u'\\InsetSpace \\space{}':u' ', u'\\InsetSpace \\thinspace{}':u' ', 
+      u'\\InsetSpace ~':u' ', u'\\SpecialChar \\-':u'', 
+      u'\\SpecialChar \\@.':u'.', u'\\SpecialChar \\ldots{}':u'…', 
+      u'\\SpecialChar \\menuseparator':u' ▷ ', 
+      u'\\SpecialChar \\nobreakdash-':u'-', u'\\SpecialChar \\slash{}':u'/', 
+      u'\\SpecialChar \\textcompwordmark{}':u'', u'\\backslash':u'\\', 
+      }
+
+  entities = {
+      u'&':u'&amp;', u'<':u'&lt;', u'>':u'&gt;', 
+      }
+
+  html = {
+      u'/>':u'>', 
+      }
+
+  iso885915 = {
+      u' ':u'&nbsp;', u' ':u'&emsp;', u' ':u'&#8197;', 
+      }
+
+  nonunicode = {
+      u' ':u' ', 
+      }
+
+class FormulaConfig(object):
+  "Configuration class from elyxer.config file"
+
+  alphacommands = {
+      u'\\AA':u'Å', u'\\AE':u'Æ', 
+      u'\\AmS':u'<span class="versalitas">AmS</span>', u'\\DH':u'Ð', 
+      u'\\L':u'Ł', u'\\O':u'Ø', u'\\OE':u'Œ', u'\\TH':u'Þ', u'\\aa':u'å', 
+      u'\\ae':u'æ', u'\\alpha':u'α', u'\\beta':u'β', u'\\delta':u'δ', 
+      u'\\dh':u'ð', u'\\epsilon':u'ϵ', u'\\eta':u'η', u'\\gamma':u'γ', 
+      u'\\i':u'ı', u'\\imath':u'ı', u'\\iota':u'ι', u'\\j':u'ȷ', 
+      u'\\jmath':u'ȷ', u'\\kappa':u'κ', u'\\l':u'ł', u'\\lambda':u'λ', 
+      u'\\mu':u'μ', u'\\nu':u'ν', u'\\o':u'ø', u'\\oe':u'œ', u'\\omega':u'ω', 
+      u'\\phi':u'φ', u'\\pi':u'π', u'\\psi':u'ψ', u'\\rho':u'ρ', 
+      u'\\sigma':u'σ', u'\\ss':u'ß', u'\\tau':u'τ', u'\\textcrh':u'ħ', 
+      u'\\th':u'þ', u'\\theta':u'θ', u'\\upsilon':u'υ', u'\\varDelta':u'∆', 
+      u'\\varGamma':u'Γ', u'\\varLambda':u'Λ', u'\\varOmega':u'Ω', 
+      u'\\varPhi':u'Φ', u'\\varPi':u'Π', u'\\varPsi':u'Ψ', u'\\varSigma':u'Σ', 
+      u'\\varTheta':u'Θ', u'\\varUpsi