summaryrefslogtreecommitdiff
path: root/sphinx/util/convert-trac.py
blob: ebb77d42054989aefb5d9d5f732ff44e7ea16318 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#!python

"""
%prog <filename>

A utility script for performing some commonly-encountered patterns in
Trac Wiki format into reStructuredText (rst).

filename is the name of the text file to be saved. If -U is not used,
the file is converted in-place and filename is also the name of the
source.
"""

from __future__ import print_function
import sys
import re
import inspect
import optparse
import shutil
import urllib2
from StringIO import StringIO


def get_options():
    global options
    parser = optparse.OptionParser(usage=inspect.cleandoc(__doc__))
    parser.add_option('-U', '--url',
                      help="Trac URL from which to retrieve source")
    options, args = parser.parse_args()
    try:
        options.filename = args.pop()
    except IndexError:
        parser.error("Filename required")

# each of the replacement functions should have a docstring
#  which is a regular expression to be matched.


def replace_external_link(matcher):
    r"\[(?P<href>(?P<scheme>\w+)\://.+?) (?P<name>.+?)\]"
    return '`{name} <{href}>`_'.format(**matcher.groupdict())


def replace_wiki_link(matcher):
    r"\[wiki\:(?P<ref>.+?) (?P<name>.+?)\]"
    return '`{name} <TODO-fix wiki target {ref}>`_'.format(
        **matcher.groupdict()
    )

# character array indexed by level for characters
heading_characters = [None, '*', '=', '-', '^']


def replace_headings(matcher):
    r"^(?P<level>=+) (?P<name>.*) (?P=level)$"
    level = len(matcher.groupdict()['level'])
    char = heading_characters[level]
    name = matcher.groupdict()['name']
    lines = [name, char * len(name)]
    if level == 1:
        lines.insert(0, char * len(name))
    return '\n'.join(lines)


def indent(block):
    add_indent = lambda s: '    ' + s
    lines = StringIO(block)
    i_lines = map(add_indent, lines)
    return ''.join(i_lines)


def replace_inline_code(matcher):
    r"\{\{\{(?P<code>[^\n]*?)\}\}\}"
    return '``{code}``'.format(**matcher.groupdict())


def replace_code_block(matcher):
    r"\{\{\{\n(?P<code>(.|\n)*?)^\}\}\}"
    return '::\n\n' + indent(matcher.groupdict()['code'])


def replace_page_outline(matcher):
    r"\[\[PageOutline\]\]\n"
    return ''


def replace_bang_symbols(matcher):
    r"!(?P<symbol>\w+)"
    return matcher.groupdict()['symbol']

# a number of the files end in
"""{{{
#!html
<h2 class='compatibility'>Older versions</h2>
}}}"""  # and everything after is garbage, so just remove it.


def remove_2x_compat_notes(matcher):
    r"\{\{\{\n#!html\n<h2(.|\n)*"
    return ''

replacements = [remove_2x_compat_notes] + \
    [func for name,
     func in globals().items() if name.startswith('replace_')]


def normalize_linebreaks(text):
    return text.replace('\r\n', '\n')


def convert_file():
    filename = options.filename
    if options.url:
        text = urllib2.urlopen(options.url).read()
        text = normalize_linebreaks(text)
    else:
        shutil.copy(filename, filename + '.bak')
        text = open(filename).read()
    # iterate over each of the replacements and execute it
    new_text = text
    for repl in replacements:
        pattern = re.compile(inspect.getdoc(repl), re.MULTILINE)
        new_text = pattern.sub(repl, new_text)

    open(filename, 'w').write(new_text)
    print("done")


def handle_command_line():
    get_options()
    convert_file()

if __name__ == '__main__':
    handle_command_line()