1 # Authors: David Goodger, Dethe Elza
2 # Contact: goodger@users.sourceforge.net
5 # Copyright: This module has been placed in the public domain.
7 """Miscellaneous directives."""
9 __docformat__
= 'reStructuredText'
14 from urllib2
import urlopen
, URLError
15 from docutils
import io
, nodes
, statemachine
, utils
16 from docutils
.parsers
.rst
import directives
, states
17 from docutils
.transforms
import misc
20 def include(name
, arguments
, options
, content
, lineno
,
21 content_offset
, block_text
, state
, state_machine
):
22 """Include a reST file as part of the content of this reST file."""
24 source
= state_machine
.input_lines
.source(
25 lineno
- state_machine
.input_offset
- 1)
26 source_dir
= os
.path
.dirname(os
.path
.abspath(source
))
27 except AttributeError:
28 # It might be that we're not actually reading from a file
29 # right now; so just read the first line of block_text
30 # instead and grab the filename from that
32 lines
=re
.compile("\n").split(process_me
)
33 path
=re
.sub(re
.compile("..\s+include::\s+"),"",lines
[0])
34 source_dir
= os
.path
.dirname(os
.path
.abspath(path
))
36 path
= ''.join(arguments
[0].splitlines())
37 if path
.find(' ') != -1:
38 error
= state_machine
.reporter
.error(
39 '"%s" directive path contains whitespace.' % name
,
40 nodes
.literal_block(block_text
, block_text
), line
=lineno
)
42 path
= os
.path
.normpath(os
.path
.join(source_dir
, path
))
43 path
= utils
.relative_path(None, path
)
44 if include_registry
.has_key(path
): return []
45 include_registry
[path
] = True
47 include_file
= io
.FileInput(
48 source_path
=path
, encoding
=state
.document
.settings
.input_encoding
,
49 handle_io_errors
=None)
50 except IOError, error
:
51 severe
= state_machine
.reporter
.severe(
52 'Problems with "%s" directive path:\n%s: %s.'
53 % (name
, error
.__class
__.__name
__, error
),
54 nodes
.literal_block(block_text
, block_text
), line
=lineno
)
56 include_text
= include_file
.read()
57 if options
.has_key('literal'):
58 literal_block
= nodes
.literal_block(include_text
, include_text
,
60 literal_block
.line
= 1
63 include_lines
= statemachine
.string2lines(include_text
,
65 state_machine
.insert_input(include_lines
, path
)
68 include
.arguments
= (1, 0, 1)
69 include
.options
= {'literal': directives
.flag
}
71 def raw(name
, arguments
, options
, content
, lineno
,
72 content_offset
, block_text
, state
, state_machine
):
74 Pass through content unchanged
76 Content is included in output based on type argument
78 Content may be included inline (content section of directive) or
79 imported from a file or url.
81 attributes
= {'format': arguments
[0]}
83 if options
.has_key('file') or options
.has_key('url'):
84 error
= state_machine
.reporter
.error(
85 '"%s" directive may not both specify an external file and '
86 'have content.' % name
,
87 nodes
.literal_block(block_text
, block_text
), line
=lineno
)
89 text
= '\n'.join(content
)
90 elif options
.has_key('file'):
91 if options
.has_key('url'):
92 error
= state_machine
.reporter
.error(
93 'The "file" and "url" options may not be simultaneously '
94 'specified for the "%s" directive.' % name
,
95 nodes
.literal_block(block_text
, block_text
), line
=lineno
)
97 source_dir
= os
.path
.dirname(
98 os
.path
.abspath(state
.document
.current_source
))
99 path
= os
.path
.normpath(os
.path
.join(source_dir
, options
['file']))
100 path
= utils
.relative_path(None, path
)
102 raw_file
= open(path
)
103 except IOError, error
:
104 severe
= state_machine
.reporter
.severe(
105 'Problems with "%s" directive path:\n%s.' % (name
, error
),
106 nodes
.literal_block(block_text
, block_text
), line
=lineno
)
108 text
= raw_file
.read()
110 attributes
['source'] = path
111 elif options
.has_key('url'):
113 raw_file
= urlopen(options
['url'])
114 except (URLError
, IOError, OSError), error
:
115 severe
= state_machine
.reporter
.severe(
116 'Problems with "%s" directive URL "%s":\n%s.'
117 % (name
, options
['url'], error
),
118 nodes
.literal_block(block_text
, block_text
), line
=lineno
)
120 text
= raw_file
.read()
122 attributes
['source'] = options
['file']
124 error
= state_machine
.reporter
.warning(
125 'The "%s" directive requires content; none supplied.' % (name
),
126 nodes
.literal_block(block_text
, block_text
), line
=lineno
)
128 raw_node
= nodes
.raw('', text
, **attributes
)
131 raw
.arguments
= (1, 0, 1)
132 raw
.options
= {'file': directives
.path
,
133 'url': directives
.path
}
136 def replace(name
, arguments
, options
, content
, lineno
,
137 content_offset
, block_text
, state
, state_machine
):
138 if not isinstance(state
, states
.SubstitutionDef
):
139 error
= state_machine
.reporter
.error(
140 'Invalid context: the "%s" directive can only be used within a '
141 'substitution definition.' % (name
),
142 nodes
.literal_block(block_text
, block_text
), line
=lineno
)
144 text
= '\n'.join(content
)
145 element
= nodes
.Element(text
)
147 state
.nested_parse(content
, content_offset
, element
)
148 if len(element
) != 1 or not isinstance(element
[0], nodes
.paragraph
):
151 if isinstance(node
, nodes
.system_message
):
152 if node
.has_key('backrefs'):
154 messages
.append(node
)
155 error
= state_machine
.reporter
.error(
156 'Error in "%s" directive: may contain a single paragraph '
157 'only.' % (name
), line
=lineno
)
158 messages
.append(error
)
161 return element
[0].children
163 error
= state_machine
.reporter
.error(
164 'The "%s" directive is empty; content required.' % (name
),
170 def unicode_directive(name
, arguments
, options
, content
, lineno
,
171 content_offset
, block_text
, state
, state_machine
):
173 Convert Unicode character codes (numbers) to characters. Codes may be
174 decimal numbers, hexadecimal numbers (prefixed by ``0x``, ``x``, ``\x``,
175 ``U+``, ``u``, or ``\u``; e.g. ``U+262E``), or XML-style numeric character
176 entities (e.g. ``☮``). Text following ".." is a comment and is
177 ignored. Spaces are ignored, and any other text remains as-is.
179 if not isinstance(state
, states
.SubstitutionDef
):
180 error
= state_machine
.reporter
.error(
181 'Invalid context: the "%s" directive can only be used within a '
182 'substitution definition.' % (name
),
183 nodes
.literal_block(block_text
, block_text
), line
=lineno
)
185 codes
= arguments
[0].split('.. ')[0].split()
186 element
= nodes
.Element()
190 element
+= nodes
.Text(unichr(int(code
)))
192 match
= unicode_pattern
.match(code
)
194 value
= match
.group(1) or match
.group(2)
195 element
+= nodes
.Text(unichr(int(value
, 16)))
197 element
+= nodes
.Text(code
)
198 except (ValueError, OverflowError), err
:
199 error
= state_machine
.reporter
.error(
200 'Invalid character code: %s\n%s: %s'
201 % (code
, err
.__class
__.__name
__, err
),
202 nodes
.literal_block(block_text
, block_text
), line
=lineno
)
204 return element
.children
206 unicode_directive
.arguments
= (1, 0, 1)
207 unicode_pattern
= re
.compile(
208 r
'(?:0x|x|\x00x|U\+?|\x00u)([0-9a-f]+)$|&#x([0-9a-f]+);$', re
.IGNORECASE
)
210 def class_directive(name
, arguments
, options
, content
, lineno
,
211 content_offset
, block_text
, state
, state_machine
):
213 class_value
= nodes
.make_id(arguments
[0])
215 pending
= nodes
.pending(misc
.ClassAttribute
,
216 {'class': class_value
, 'directive': name
},
218 state_machine
.document
.note_pending(pending
)
221 error
= state_machine
.reporter
.error(
222 'Invalid class attribute value for "%s" directive: %s'
223 % (name
, arguments
[0]),
224 nodes
.literal_block(block_text
, block_text
), line
=lineno
)
227 class_directive
.arguments
= (1, 0, 0)
228 class_directive
.content
= 1
230 def directive_test_function(name
, arguments
, options
, content
, lineno
,
231 content_offset
, block_text
, state
, state_machine
):
233 text
= '\n'.join(content
)
234 info
= state_machine
.reporter
.info(
235 'Directive processed. Type="%s", arguments=%r, options=%r, '
236 'content:' % (name
, arguments
, options
),
237 nodes
.literal_block(text
, text
), line
=lineno
)
239 info
= state_machine
.reporter
.info(
240 'Directive processed. Type="%s", arguments=%r, options=%r, '
241 'content: None' % (name
, arguments
, options
), line
=lineno
)
244 directive_test_function
.arguments
= (0, 1, 1)
245 directive_test_function
.options
= {'option': directives
.unchanged_required
}
246 directive_test_function
.content
= 1