announce change of default behaviour and deprecation of `handle_io_errors`
[docutils/kirr.git] / sandbox / blais / rst-literals / rst-literals
bloba31a8419e713a9e0fdccf815b12966f09e226cb6
1 #!/usr/bin/env python
2 """
3 Extracts literal blocks from a ReST document and save them out to files.
5 Each literal block can be typed by placing a string like ``#!<type>`` on the
6 first line of the literal block. This can be done, for example, #to mark some
7 literal blocks as SQL code. Literal blocks that are not typed are #associated to
8 the type 'generic'.
10 If you only want to output a specific type of literal block, you can decide
11 which one to output by using the -t option. By default, only the generic blocks
12 are output.
14 .. note::
16 The author uses for multiple purposes, in a way that reminds him of literate
17 programming. For example, when designing a database schema, an interesting
18 use case is to include the schema in a document that describes the entities
19 in detail, with their intended purpose. The code itself goes in the literal
20 blocks. You can them easily extract the SQL string that contains all the SQL
21 data definitions to setup your database schema.
23 Here is an example::
25 #!sql
26 CREATE TABLE price (
28 id SERIAL,
29 asset_id INTEGER REFERENCES asset(id),
30 date TIMESTAMP WITHOUT TIME ZONE,
31 price NUMERIC(16, 6),
33 PRIMARY KEY (id)
36 If you run rst-literals on this text you will get only the literal block under
37 #!sql.
39 Blocks may also be named. If you mark it up like this::
41 #!sql price-table.sql
42 CREATE TABLE price (
44 you can use the `` --save-named-blocks `` option to have the blocks extracted to
45 corresponding files. Note that you may just use the name if you like::
47 #! example1.sql
48 CREATE TABLE price (
52 """
53 __author__ = 'Martin Blais <blais@furius.ca>'
55 # stdlib imports
56 import sys, re
58 # docutils imports
59 from docutils import core, nodes
62 class Visitor(nodes.SparseNodeVisitor):
64 def __init__(self, *args):
65 nodes.SparseNodeVisitor.__init__(self, *args)
66 self.chunks = []
68 def visit_literal_block(self, node):
69 text = node.astext()
70 mo = re.match(u'[ \t]*#!([^ \t]*)([ \t](.*))?\n', text)
71 if mo:
72 typ, name = mo.group(1, 3)
73 text = text[mo.end(3):]
74 else:
75 typ, name = 'generic', None
77 self.chunks.append( (typ, name, text) )
79 def main():
80 import optparse
81 parser = optparse.OptionParser(__doc__.strip())
83 parser.add_option('-t', '--type', action='store',
84 default='generic',
85 help="Chunk type to extract")
87 parser.add_option('-s', '--save-named-blocks', action='store_true',
88 help="Save all named blocks to files (in CWD).")
90 opts, args = parser.parse_args()
92 if len(args) != 1:
93 parser.error("You must specify a filename to process.")
94 fn, = args
96 text = open(fn).read()
97 document = core.publish_doctree(
98 source_path=fn,
99 source=text,
100 reader_name='standalone',
101 parser_name='restructuredtext',
102 settings_overrides={
103 'input_encoding': 'UTF-8',
107 v = Visitor(document)
108 document.walk(v)
110 for typ, name, text in v.chunks:
111 if opts.type and typ != opts.type:
112 continue
113 output_text(text, sys.stdout)
115 for typ, name, text in v.chunks:
116 if name and opts.save_named_blocks:
117 output_text(text, open(name, 'w'))
120 def output_text(text, ss):
121 "Write out the string 'text' as a block to stream 'ss')."
122 write = ss.write
123 write(text)
124 write('\n')
125 if text[-1] != '\n':
126 write('\n')
129 if __name__ == '__main__':
130 main()