~ubuntu-branches/debian/stretch/s3ql/stretch

« back to all changes in this revision

Viewing changes to util/sphinx_pipeinclude.py

  • Committer: Bazaar Package Importer
  • Author(s): Nikolaus Rath
  • Date: 2011-07-01 14:02:17 UTC
  • Revision ID: james.westby@ubuntu.com-20110701140217-cyyclk7tusagxucf
Tags: upstream-1.0.1
ImportĀ upstreamĀ versionĀ 1.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
'''
 
3
sphinx_pipe.py - this file is part of S3QL (http://s3ql.googlecode.com)
 
4
 
 
5
Implements a Sphinx extension that provides a `pipeinclude` directive
 
6
to include the output of a program.
 
7
 
 
8
 
 
9
Copyright (C) 2008-2011 Nikolaus Rath <Nikolaus@rath.org>
 
10
 
 
11
This program can be distributed under the terms of the GNU LGPL.
 
12
'''
 
13
 
 
14
from docutils.parsers.rst.directives.misc import Include
 
15
import subprocess
 
16
import shlex
 
17
from docutils import io, nodes, statemachine
 
18
import os.path
 
19
 
 
20
class PipeInclude(Include):
 
21
    """
 
22
    Include program output as ReST source.
 
23
    """
 
24
 
 
25
    def run(self):   
 
26
        source = self.state_machine.input_lines.source(
 
27
            self.lineno - self.state_machine.input_offset - 1)
 
28
        source_dir = os.path.dirname(os.path.abspath(source))
 
29
        
 
30
        command = self.arguments[0].encode('UTF-8')
 
31
        encoding = self.options.get(
 
32
            'encoding', self.state.document.settings.input_encoding)
 
33
        tab_width = self.options.get(
 
34
            'tab-width', self.state.document.settings.tab_width)
 
35
        
 
36
        try:
 
37
            child = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE,
 
38
                                     cwd=source_dir)
 
39
            include_file = io.FileInput(
 
40
                source=child.stdout, encoding=encoding,
 
41
                error_handler=(self.state.document.settings.\
 
42
                               input_encoding_error_handler),
 
43
                handle_io_errors=None)
 
44
        except IOError, error:
 
45
            raise self.severe('Problems with "%s" directive path:\n%s: %s.' %
 
46
                        (self.name, error.__class__.__name__, str(error)))
 
47
            # Hack: Since Python 2.6, the string interpolation returns a
 
48
            # unicode object if one of the supplied %s replacements is a
 
49
            # unicode object. IOError has no `__unicode__` method and the
 
50
            # fallback `__repr__` does not report the file name. Explicitely
 
51
            # converting to str fixes this for now::
 
52
            #   print '%s\n%s\n%s\n' %(error, str(error), repr(error))
 
53
        startline = self.options.get('start-line', None)
 
54
        endline = self.options.get('end-line', None)
 
55
        try:
 
56
            if startline or (endline is not None):
 
57
                include_lines = include_file.readlines()
 
58
                include_text = ''.join(include_lines[startline:endline])
 
59
            else:
 
60
                include_text = include_file.read()
 
61
        except UnicodeError, error:
 
62
            raise self.severe(
 
63
                'Problem with "%s" directive:\n%s: %s'
 
64
                % (self.name, error.__class__.__name__, error))
 
65
        # start-after/end-before: no restrictions on newlines in match-text,
 
66
        # and no restrictions on matching inside lines vs. line boundaries
 
67
        after_text = self.options.get('start-after', None)
 
68
        if after_text:
 
69
            # skip content in include_text before *and incl.* a matching text
 
70
            after_index = include_text.find(after_text)
 
71
            if after_index < 0:
 
72
                raise self.severe('Problem with "start-after" option of "%s" '
 
73
                                  'directive:\nText not found.' % self.name)
 
74
            include_text = include_text[after_index + len(after_text):]
 
75
        before_text = self.options.get('end-before', None)
 
76
        if before_text:
 
77
            # skip content in include_text after *and incl.* a matching text
 
78
            before_index = include_text.find(before_text)
 
79
            if before_index < 0:
 
80
                raise self.severe('Problem with "end-before" option of "%s" '
 
81
                                  'directive:\nText not found.' % self.name)
 
82
            include_text = include_text[:before_index]
 
83
        if 'literal' in self.options:
 
84
            # Convert tabs to spaces, if `tab_width` is positive.
 
85
            if tab_width >= 0:
 
86
                text = include_text.expandtabs(tab_width)
 
87
            else:
 
88
                text = include_text
 
89
            literal_block = nodes.literal_block(include_text, text, 
 
90
                                                source=command)
 
91
            literal_block.line = 1
 
92
            return [literal_block]
 
93
        else:
 
94
            include_lines = statemachine.string2lines(
 
95
                include_text, tab_width, convert_whitespace=1)
 
96
            self.state_machine.insert_input(include_lines, command)
 
97
            return []
 
98
 
 
99
 
 
100
def setup(app):
 
101
    app.add_directive('pipeinclude', PipeInclude)
 
102