buildframework/helium/external/python/lib/common/docutils-0.5-py2.5.egg/docutils/parsers/rst/directives/misc.py
changeset 179 d8ac696cc51f
equal deleted inserted replaced
1:be27ed110b50 179:d8ac696cc51f
       
     1 # $Id: misc.py 5015 2007-03-12 20:25:40Z wiemann $
       
     2 # Authors: David Goodger <goodger@python.org>; Dethe Elza
       
     3 # Copyright: This module has been placed in the public domain.
       
     4 
       
     5 """Miscellaneous directives."""
       
     6 
       
     7 __docformat__ = 'reStructuredText'
       
     8 
       
     9 import sys
       
    10 import os.path
       
    11 import re
       
    12 import time
       
    13 from docutils import io, nodes, statemachine, utils
       
    14 from docutils.parsers.rst import Directive, convert_directive_function
       
    15 from docutils.parsers.rst import directives, roles, states
       
    16 from docutils.transforms import misc
       
    17 
       
    18 
       
    19 class Include(Directive):
       
    20 
       
    21     """
       
    22     Include content read from a separate source file.
       
    23 
       
    24     Content may be parsed by the parser, or included as a literal
       
    25     block.  The encoding of the included file can be specified.  Only
       
    26     a part of the given file argument may be included by specifying
       
    27     text to match before and/or after the text to be used.
       
    28     """
       
    29 
       
    30     required_arguments = 1
       
    31     optional_arguments = 0
       
    32     final_argument_whitespace = True
       
    33     option_spec = {'literal': directives.flag,
       
    34                    'encoding': directives.encoding,
       
    35                    'start-after': directives.unchanged_required,
       
    36                    'end-before': directives.unchanged_required}
       
    37 
       
    38     standard_include_path = os.path.join(os.path.dirname(states.__file__),
       
    39                                          'include')
       
    40 
       
    41     def run(self):
       
    42         """Include a reST file as part of the content of this reST file."""
       
    43         if not self.state.document.settings.file_insertion_enabled:
       
    44             raise self.warning('"%s" directive disabled.' % self.name)
       
    45         source = self.state_machine.input_lines.source(
       
    46             self.lineno - self.state_machine.input_offset - 1)
       
    47         source_dir = os.path.dirname(os.path.abspath(source))
       
    48         path = directives.path(self.arguments[0])
       
    49         if path.startswith('<') and path.endswith('>'):
       
    50             path = os.path.join(self.standard_include_path, path[1:-1])
       
    51         path = os.path.normpath(os.path.join(source_dir, path))
       
    52         path = utils.relative_path(None, path)
       
    53         encoding = self.options.get(
       
    54             'encoding', self.state.document.settings.input_encoding)
       
    55         try:
       
    56             self.state.document.settings.record_dependencies.add(path)
       
    57             include_file = io.FileInput(
       
    58                 source_path=path, encoding=encoding,
       
    59                 error_handler=(self.state.document.settings.\
       
    60                                input_encoding_error_handler),
       
    61                 handle_io_errors=None)
       
    62         except IOError, error:
       
    63             raise self.severe('Problems with "%s" directive path:\n%s: %s.'
       
    64                               % (self.name, error.__class__.__name__, error))
       
    65         try:
       
    66             include_text = include_file.read()
       
    67         except UnicodeError, error:
       
    68             raise self.severe(
       
    69                 'Problem with "%s" directive:\n%s: %s'
       
    70                 % (self.name, error.__class__.__name__, error))
       
    71         # start-after/end-before: no restrictions on newlines in match-text,
       
    72         # and no restrictions on matching inside lines vs. line boundaries
       
    73         after_text = self.options.get('start-after', None)
       
    74         if after_text:
       
    75             # skip content in include_text before *and incl.* a matching text
       
    76             after_index = include_text.find(after_text)
       
    77             if after_index < 0:
       
    78                 raise self.severe('Problem with "start-after" option of "%s" '
       
    79                                   'directive:\nText not found.' % self.name)
       
    80             include_text = include_text[after_index + len(after_text):]
       
    81         before_text = self.options.get('end-before', None)
       
    82         if before_text:
       
    83             # skip content in include_text after *and incl.* a matching text
       
    84             before_index = include_text.find(before_text)
       
    85             if before_index < 0:
       
    86                 raise self.severe('Problem with "end-before" option of "%s" '
       
    87                                   'directive:\nText not found.' % self.name)
       
    88             include_text = include_text[:before_index]
       
    89         if self.options.has_key('literal'):
       
    90             literal_block = nodes.literal_block(include_text, include_text,
       
    91                                                 source=path)
       
    92             literal_block.line = 1
       
    93             return [literal_block]
       
    94         else:
       
    95             include_lines = statemachine.string2lines(include_text,
       
    96                                                       convert_whitespace=1)
       
    97             self.state_machine.insert_input(include_lines, path)
       
    98             return []
       
    99 
       
   100 
       
   101 class Raw(Directive):
       
   102 
       
   103     """
       
   104     Pass through content unchanged
       
   105 
       
   106     Content is included in output based on type argument
       
   107 
       
   108     Content may be included inline (content section of directive) or
       
   109     imported from a file or url.
       
   110     """
       
   111 
       
   112     required_arguments = 1
       
   113     optional_arguments = 0
       
   114     final_argument_whitespace = True
       
   115     option_spec = {'file': directives.path,
       
   116                    'url': directives.uri,
       
   117                    'encoding': directives.encoding}
       
   118     has_content = True
       
   119 
       
   120     def run(self):
       
   121         if (not self.state.document.settings.raw_enabled
       
   122             or (not self.state.document.settings.file_insertion_enabled
       
   123                 and (self.options.has_key('file')
       
   124                      or self.options.has_key('url')))):
       
   125             raise self.warning('"%s" directive disabled.' % self.name)
       
   126         attributes = {'format': ' '.join(self.arguments[0].lower().split())}
       
   127         encoding = self.options.get(
       
   128             'encoding', self.state.document.settings.input_encoding)
       
   129         if self.content:
       
   130             if self.options.has_key('file') or self.options.has_key('url'):
       
   131                 raise self.error(
       
   132                     '"%s" directive may not both specify an external file '
       
   133                     'and have content.' % self.name)
       
   134             text = '\n'.join(self.content)
       
   135         elif self.options.has_key('file'):
       
   136             if self.options.has_key('url'):
       
   137                 raise self.error(
       
   138                     'The "file" and "url" options may not be simultaneously '
       
   139                     'specified for the "%s" directive.' % self.name)
       
   140             source_dir = os.path.dirname(
       
   141                 os.path.abspath(self.state.document.current_source))
       
   142             path = os.path.normpath(os.path.join(source_dir,
       
   143                                                  self.options['file']))
       
   144             path = utils.relative_path(None, path)
       
   145             try:
       
   146                 self.state.document.settings.record_dependencies.add(path)
       
   147                 raw_file = io.FileInput(
       
   148                     source_path=path, encoding=encoding,
       
   149                     error_handler=(self.state.document.settings.\
       
   150                                    input_encoding_error_handler),
       
   151                     handle_io_errors=None)
       
   152             except IOError, error:
       
   153                 raise self.severe('Problems with "%s" directive path:\n%s.'
       
   154                                   % (self.name, error))
       
   155             try:
       
   156                 text = raw_file.read()
       
   157             except UnicodeError, error:
       
   158                 raise self.severe(
       
   159                     'Problem with "%s" directive:\n%s: %s'
       
   160                     % (self.name, error.__class__.__name__, error))
       
   161             attributes['source'] = path
       
   162         elif self.options.has_key('url'):
       
   163             source = self.options['url']
       
   164             # Do not import urllib2 at the top of the module because
       
   165             # it may fail due to broken SSL dependencies, and it takes
       
   166             # about 0.15 seconds to load.
       
   167             import urllib2
       
   168             try:
       
   169                 raw_text = urllib2.urlopen(source).read()
       
   170             except (urllib2.URLError, IOError, OSError), error:
       
   171                 raise self.severe(
       
   172                     'Problems with "%s" directive URL "%s":\n%s.'
       
   173                     % (self.name, self.options['url'], error))
       
   174             raw_file = io.StringInput(
       
   175                 source=raw_text, source_path=source, encoding=encoding,
       
   176                 error_handler=(self.state.document.settings.\
       
   177                                input_encoding_error_handler))
       
   178             try:
       
   179                 text = raw_file.read()
       
   180             except UnicodeError, error:
       
   181                 raise self.severe(
       
   182                     'Problem with "%s" directive:\n%s: %s'
       
   183                     % (self.name, error.__class__.__name__, error))
       
   184             attributes['source'] = source
       
   185         else:
       
   186             # This will always fail because there is no content.
       
   187             self.assert_has_content()
       
   188         raw_node = nodes.raw('', text, **attributes)
       
   189         return [raw_node]
       
   190 
       
   191 
       
   192 class Replace(Directive):
       
   193 
       
   194     has_content = True
       
   195 
       
   196     def run(self):
       
   197         if not isinstance(self.state, states.SubstitutionDef):
       
   198             raise self.error(
       
   199                 'Invalid context: the "%s" directive can only be used within '
       
   200                 'a substitution definition.' % self.name)
       
   201         self.assert_has_content()
       
   202         text = '\n'.join(self.content)
       
   203         element = nodes.Element(text)
       
   204         self.state.nested_parse(self.content, self.content_offset,
       
   205                                 element)
       
   206         if ( len(element) != 1
       
   207              or not isinstance(element[0], nodes.paragraph)):
       
   208             messages = []
       
   209             for node in element:
       
   210                 if isinstance(node, nodes.system_message):
       
   211                     node['backrefs'] = []
       
   212                     messages.append(node)
       
   213             error = self.state_machine.reporter.error(
       
   214                 'Error in "%s" directive: may contain a single paragraph '
       
   215                 'only.' % (self.name), line=self.lineno)
       
   216             messages.append(error)
       
   217             return messages
       
   218         else:
       
   219             return element[0].children
       
   220 
       
   221 
       
   222 class Unicode(Directive):
       
   223 
       
   224     r"""
       
   225     Convert Unicode character codes (numbers) to characters.  Codes may be
       
   226     decimal numbers, hexadecimal numbers (prefixed by ``0x``, ``x``, ``\x``,
       
   227     ``U+``, ``u``, or ``\u``; e.g. ``U+262E``), or XML-style numeric character
       
   228     entities (e.g. ``&#x262E;``).  Text following ".." is a comment and is
       
   229     ignored.  Spaces are ignored, and any other text remains as-is.
       
   230     """
       
   231 
       
   232     required_arguments = 1
       
   233     optional_arguments = 0
       
   234     final_argument_whitespace = True
       
   235     option_spec = {'trim': directives.flag,
       
   236                    'ltrim': directives.flag,
       
   237                    'rtrim': directives.flag}
       
   238 
       
   239     comment_pattern = re.compile(r'( |\n|^)\.\. ')
       
   240 
       
   241     def run(self):
       
   242         if not isinstance(self.state, states.SubstitutionDef):
       
   243             raise self.error(
       
   244                 'Invalid context: the "%s" directive can only be used within '
       
   245                 'a substitution definition.' % self.name)
       
   246         substitution_definition = self.state_machine.node
       
   247         if self.options.has_key('trim'):
       
   248             substitution_definition.attributes['ltrim'] = 1
       
   249             substitution_definition.attributes['rtrim'] = 1
       
   250         if self.options.has_key('ltrim'):
       
   251             substitution_definition.attributes['ltrim'] = 1
       
   252         if self.options.has_key('rtrim'):
       
   253             substitution_definition.attributes['rtrim'] = 1
       
   254         codes = self.comment_pattern.split(self.arguments[0])[0].split()
       
   255         element = nodes.Element()
       
   256         for code in codes:
       
   257             try:
       
   258                 decoded = directives.unicode_code(code)
       
   259             except ValueError, err:
       
   260                 raise self.error(
       
   261                     'Invalid character code: %s\n%s: %s'
       
   262                     % (code, err.__class__.__name__, err))
       
   263             element += nodes.Text(decoded)
       
   264         return element.children
       
   265 
       
   266 
       
   267 class Class(Directive):
       
   268 
       
   269     """
       
   270     Set a "class" attribute on the directive content or the next element.
       
   271     When applied to the next element, a "pending" element is inserted, and a
       
   272     transform does the work later.
       
   273     """
       
   274 
       
   275     required_arguments = 1
       
   276     optional_arguments = 0
       
   277     final_argument_whitespace = True
       
   278     has_content = True
       
   279 
       
   280     def run(self):
       
   281         try:
       
   282             class_value = directives.class_option(self.arguments[0])
       
   283         except ValueError:
       
   284             raise self.error(
       
   285                 'Invalid class attribute value for "%s" directive: "%s".'
       
   286                 % (self.name, self.arguments[0]))
       
   287         node_list = []
       
   288         if self.content:
       
   289             container = nodes.Element()
       
   290             self.state.nested_parse(self.content, self.content_offset,
       
   291                                     container)
       
   292             for node in container:
       
   293                 node['classes'].extend(class_value)
       
   294             node_list.extend(container.children)
       
   295         else:
       
   296             pending = nodes.pending(
       
   297                 misc.ClassAttribute,
       
   298                 {'class': class_value, 'directive': self.name},
       
   299                 self.block_text)
       
   300             self.state_machine.document.note_pending(pending)
       
   301             node_list.append(pending)
       
   302         return node_list
       
   303 
       
   304 
       
   305 class Role(Directive):
       
   306 
       
   307     has_content = True
       
   308 
       
   309     argument_pattern = re.compile(r'(%s)\s*(\(\s*(%s)\s*\)\s*)?$'
       
   310                                   % ((states.Inliner.simplename,) * 2))
       
   311 
       
   312     def run(self):
       
   313         """Dynamically create and register a custom interpreted text role."""
       
   314         if self.content_offset > self.lineno or not self.content:
       
   315             raise self.error('"%s" directive requires arguments on the first '
       
   316                              'line.' % self.name)
       
   317         args = self.content[0]
       
   318         match = self.argument_pattern.match(args)
       
   319         if not match:
       
   320             raise self.error('"%s" directive arguments not valid role names: '
       
   321                              '"%s".' % (self.name, args))
       
   322         new_role_name = match.group(1)
       
   323         base_role_name = match.group(3)
       
   324         messages = []
       
   325         if base_role_name:
       
   326             base_role, messages = roles.role(
       
   327                 base_role_name, self.state_machine.language, self.lineno,
       
   328                 self.state.reporter)
       
   329             if base_role is None:
       
   330                 error = self.state.reporter.error(
       
   331                     'Unknown interpreted text role "%s".' % base_role_name,
       
   332                     nodes.literal_block(self.block_text, self.block_text),
       
   333                     line=self.lineno)
       
   334                 return messages + [error]
       
   335         else:
       
   336             base_role = roles.generic_custom_role
       
   337         assert not hasattr(base_role, 'arguments'), (
       
   338             'Supplemental directive arguments for "%s" directive not '
       
   339             'supported (specified by "%r" role).' % (self.name, base_role))
       
   340         try:
       
   341             converted_role = convert_directive_function(base_role)
       
   342             (arguments, options, content, content_offset) = (
       
   343                 self.state.parse_directive_block(
       
   344                 self.content[1:], self.content_offset, converted_role,
       
   345                 option_presets={}))
       
   346         except states.MarkupError, detail:
       
   347             error = self.state_machine.reporter.error(
       
   348                 'Error in "%s" directive:\n%s.' % (self.name, detail),
       
   349                 nodes.literal_block(self.block_text, self.block_text),
       
   350                 line=self.lineno)
       
   351             return messages + [error]
       
   352         if not options.has_key('class'):
       
   353             try:
       
   354                 options['class'] = directives.class_option(new_role_name)
       
   355             except ValueError, detail:
       
   356                 error = self.state_machine.reporter.error(
       
   357                     'Invalid argument for "%s" directive:\n%s.'
       
   358                     % (self.name, detail), nodes.literal_block(
       
   359                     self.block_text, self.block_text), line=self.lineno)
       
   360                 return messages + [error]
       
   361         role = roles.CustomRole(new_role_name, base_role, options, content)
       
   362         roles.register_local_role(new_role_name, role)
       
   363         return messages
       
   364 
       
   365 
       
   366 class DefaultRole(Directive):
       
   367 
       
   368     """Set the default interpreted text role."""
       
   369 
       
   370     required_arguments = 0
       
   371     optional_arguments = 1
       
   372     final_argument_whitespace = False
       
   373 
       
   374     def run(self):
       
   375         if not self.arguments:
       
   376             if roles._roles.has_key(''):
       
   377                 # restore the "default" default role
       
   378                 del roles._roles['']
       
   379             return []
       
   380         role_name = self.arguments[0]
       
   381         role, messages = roles.role(role_name, self.state_machine.language,
       
   382                                     self.lineno, self.state.reporter)
       
   383         if role is None:
       
   384             error = self.state.reporter.error(
       
   385                 'Unknown interpreted text role "%s".' % role_name,
       
   386                 nodes.literal_block(self.block_text, self.block_text),
       
   387                 line=self.lineno)
       
   388             return messages + [error]
       
   389         roles._roles[''] = role
       
   390         # @@@ should this be local to the document, not the parser?
       
   391         return messages
       
   392 
       
   393 
       
   394 class Title(Directive):
       
   395 
       
   396     required_arguments = 1
       
   397     optional_arguments = 0
       
   398     final_argument_whitespace = True
       
   399 
       
   400     def run(self):
       
   401         self.state_machine.document['title'] = self.arguments[0]
       
   402         return []
       
   403 
       
   404 
       
   405 class Date(Directive):
       
   406 
       
   407     has_content = True
       
   408 
       
   409     def run(self):
       
   410         if not isinstance(self.state, states.SubstitutionDef):
       
   411             raise self.error(
       
   412                 'Invalid context: the "%s" directive can only be used within '
       
   413                 'a substitution definition.' % self.name)
       
   414         format = '\n'.join(self.content) or '%Y-%m-%d'
       
   415         text = time.strftime(format)
       
   416         return [nodes.Text(text)]
       
   417 
       
   418 
       
   419 class TestDirective(Directive):
       
   420 
       
   421     """This directive is useful only for testing purposes."""
       
   422 
       
   423     required_arguments = 0
       
   424     optional_arguments = 1
       
   425     final_argument_whitespace = True
       
   426     option_spec = {'option': directives.unchanged_required}
       
   427     has_content = True
       
   428 
       
   429     def run(self):
       
   430         if self.content:
       
   431             text = '\n'.join(self.content)
       
   432             info = self.state_machine.reporter.info(
       
   433                 'Directive processed. Type="%s", arguments=%r, options=%r, '
       
   434                 'content:' % (self.name, self.arguments, self.options),
       
   435                 nodes.literal_block(text, text), line=self.lineno)
       
   436         else:
       
   437             info = self.state_machine.reporter.info(
       
   438                 'Directive processed. Type="%s", arguments=%r, options=%r, '
       
   439                 'content: None' % (self.name, self.arguments, self.options),
       
   440                 line=self.lineno)
       
   441         return [info]
       
   442 
       
   443 # Old-style, functional definition:
       
   444 #
       
   445 # def directive_test_function(name, arguments, options, content, lineno,
       
   446 #                             content_offset, block_text, state, state_machine):
       
   447 #     """This directive is useful only for testing purposes."""
       
   448 #     if content:
       
   449 #         text = '\n'.join(content)
       
   450 #         info = state_machine.reporter.info(
       
   451 #             'Directive processed. Type="%s", arguments=%r, options=%r, '
       
   452 #             'content:' % (name, arguments, options),
       
   453 #             nodes.literal_block(text, text), line=lineno)
       
   454 #     else:
       
   455 #         info = state_machine.reporter.info(
       
   456 #             'Directive processed. Type="%s", arguments=%r, options=%r, '
       
   457 #             'content: None' % (name, arguments, options), line=lineno)
       
   458 #     return [info]
       
   459 #
       
   460 # directive_test_function.arguments = (0, 1, 1)
       
   461 # directive_test_function.options = {'option': directives.unchanged_required}
       
   462 # directive_test_function.content = 1