sphinx-to-rst.py 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. from __future__ import print_function, unicode_literals
  4. import codecs
  5. import os
  6. import re
  7. import sys
  8. from collections import Callable
  9. dirname = ''
  10. RE_CODE_BLOCK = re.compile(r'.. code-block:: (.+?)\s*$')
  11. RE_INCLUDE = re.compile(r'.. include:: (.+?)\s*$')
  12. RE_REFERENCE = re.compile(r':(.+?):`(.+?)`')
  13. UNITABLE = {'…': '...'}
  14. X = re.compile(re.escape('…'))
  15. HEADER = re.compile('^[\=\~\-]+$')
  16. UNIRE = re.compile('|'.join(re.escape(p) for p in UNITABLE),
  17. re.UNICODE)
  18. def _replace_handler(match, key=UNITABLE.__getitem__):
  19. return key(match.group(0))
  20. def include_file(lines, pos, match):
  21. global dirname
  22. orig_filename = match.groups()[0]
  23. filename = os.path.join(dirname, orig_filename)
  24. fh = codecs.open(filename, encoding='utf-8')
  25. try:
  26. old_dirname = dirname
  27. dirname = os.path.dirname(orig_filename)
  28. try:
  29. lines[pos] = sphinx_to_rst(fh)
  30. finally:
  31. dirname = old_dirname
  32. finally:
  33. fh.close()
  34. def asciify(lines):
  35. prev_diff = None
  36. for line in lines:
  37. new_line = UNIRE.sub(_replace_handler, line)
  38. if prev_diff and HEADER.match(new_line):
  39. new_line = ''.join([
  40. new_line.rstrip(), new_line[0] * prev_diff, '\n'])
  41. prev_diff = len(new_line) - len(line)
  42. yield new_line.encode('ascii')
  43. def replace_code_block(lines, pos, match):
  44. lines[pos] = ''
  45. curpos = pos - 1
  46. # Find the first previous line with text to append "::" to it.
  47. while True:
  48. prev_line = lines[curpos]
  49. if not prev_line.isspace():
  50. prev_line_with_text = curpos
  51. break
  52. curpos -= 1
  53. if lines[prev_line_with_text].endswith(':'):
  54. lines[prev_line_with_text] += ':'
  55. else:
  56. lines[prev_line_with_text] += '::'
  57. TO_RST_MAP = {RE_CODE_BLOCK: replace_code_block,
  58. RE_REFERENCE: r'``\2``',
  59. RE_INCLUDE: include_file}
  60. def _process(lines):
  61. lines = list(lines) # non-destructive
  62. for i, line in enumerate(lines):
  63. for regex, alt in TO_RST_MAP.items():
  64. if isinstance(alt, Callable):
  65. match = regex.match(line)
  66. if match:
  67. alt(lines, i, match)
  68. line = lines[i]
  69. else:
  70. lines[i] = regex.sub(alt, line)
  71. return asciify(lines)
  72. def sphinx_to_rst(fh):
  73. return ''.join(_process(fh))
  74. if __name__ == '__main__':
  75. global dirname
  76. dirname = os.path.dirname(sys.argv[1])
  77. fh = codecs.open(sys.argv[1], encoding='utf-8')
  78. try:
  79. print(sphinx_to_rst(fh))
  80. finally:
  81. fh.close()