/tools/trac2rst.py
Python | 127 lines | 115 code | 6 blank | 6 comment | 0 complexity | 8c93c9d5ba9572fa57d6b398ac7a9eaf MD5 | raw file
Possible License(s): BSD-3-Clause
- #!/usr/bin/env python
- ''' Script to do very basic conversion of TRAC to reST format '''
- import sys
- import re
- rst_section_levels = ['*', '=', '-', '~', '^']
- heading_re = re.compile('^(=+) +([^=]+) +(=+)')
- enumerate_re = re.compile(r'^ +(\d+\)) +(.*)')
- link_re = re.compile(r'\[+ *(http://[\w.\-~/]+) +(.+?) *\]+')
- italic_re = re.compile(r"'''(.+?)'''")
- bold_re = re.compile(r"''(.+?)''")
- inpre_re = re.compile(r"{{{(.+?)}}}")
- outprestart_re = re.compile(r"^ *{{{ *$")
- outprestop_re = re.compile(r"^ *}}} *$")
- define_re = re.compile("^([^ ])(.*?)::")
- class state_object(object):
- def __init__(self, lines, indent=''):
- self.lines = lines
- self.indent = indent
- def add_line(self, line):
- self.lines.append(self.indent+line)
- def __call__(self, line):
- raise NotImplementedError
- def __iter__(self):
- for line in self.lines:
- yield line
- class preformatted_state(state_object):
- ''' State function for within preformatted blocks '''
- def __call__(self, line):
- if outprestop_re.match(line):
- self.add_line('')
- return standard_state(self.lines)
- self.add_line(' ' + line)
- return self
- def std_subs(line):
- ''' standard in-line substitutions '''
- line = link_re.sub(r'`\2 <\1>`_', line)
- line = italic_re.sub(r'*\1*', line)
- line = bold_re.sub(r'**\1**', line)
- line = inpre_re.sub(r'``\1``', line)
- return line
- class standard_state(state_object):
- def __call__(self, line):
- ''' State function for within normal text '''
- # beginning preformat block?
- if outprestart_re.match(line):
- self.add_line('::')
- self.add_line('')
- return preformatted_state(self.lines)
- # Heading
- hmatch = heading_re.match(line)
- if hmatch:
- eq1, heading, eq2 = hmatch.groups()
- if len(eq1) == len(eq2):
- self.add_line(heading)
- self.add_line(rst_section_levels[len(eq1)] * len(heading))
- return self
- if line.startswith(' *'):
- line = line[1:]
- line = enumerate_re.sub(r'#. \2', line)
- line = define_re.sub(r'\1\2', line)
- line = std_subs(line)
- self.add_line(line)
- return self
- def trac2rst(linesource):
- ''' Process trac line source
- A simple finite state machine
- >>> lines = ['Hello', '= Heading1 =', '=Heading2=', '== Heading 3 ==']
- >>> trac2rst(lines)
- ['Hello', 'Heading1', '========', '=Heading2=', 'Heading 3', '---------']
- >>> trac2rst([' * bullet point'])
- ['* bullet point']
- >>> trac2rst([' 33 not numbered'])
- [' 33 not numbered']
- >>> trac2rst([' 33) numbered'])
- ['#. numbered']
- >>> trac2rst(['some text then [http://www.python.org/doc a link], then text'])
- ['some text then `a link <http://www.python.org/doc>`_, then text']
- >>> line = 'text [http://www.python.org python] ' + \
- 'text [http://www.scipy.org scipy] '
- >>> trac2rst([line])
- ['text `python <http://www.python.org>`_ text `scipy <http://www.scipy.org>`_']
- >>> # The next line conceals the triple quotes from the docstring parser
- >>> trac2rst([r"Some %sitalic%s text, then %smore%s" % (("'"*3,) * 4)])
- ['Some *italic* text, then *more*']
- >>> trac2rst([r"Some ''bold'' text, then ''more''"])
- ['Some **bold** text, then **more**']
- >>> # inline preformatted text
- >>> trac2rst(['here is some {{{preformatted text}}} (end)'])
- ['here is some ``preformatted text`` (end)']
- >>> # multiline preformatted
- >>> trac2rst(['','{{{','= preformatted =', ' * text', '}}}'])
- ['', '::', '', ' = preformatted =', ' * text', '']
- >>> # define
- >>> trac2rst(['a definition::', ' some explanation'])
- ['a definition', ' some explanation']
- '''
- processor = standard_state([])
- for line in linesource:
- line = line.rstrip()
- processor = processor(line)
- return processor.lines
- if __name__ == '__main__':
- try:
- infile = sys.argv[1]
- except IndexError:
- raise OSError('Need input file')
- lines = trac2rst(file(infile))
- print '\n'.join(lines)