nbpy.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. """Read and write notebooks as regular .py files.
  2. Authors:
  3. * Brian Granger
  4. """
  5. #-----------------------------------------------------------------------------
  6. # Copyright (C) 2008-2011 The IPython Development Team
  7. #
  8. # Distributed under the terms of the BSD License. The full license is in
  9. # the file COPYING, distributed as part of this software.
  10. #-----------------------------------------------------------------------------
  11. #-----------------------------------------------------------------------------
  12. # Imports
  13. #-----------------------------------------------------------------------------
  14. import re
  15. from ipython_genutils.py3compat import unicode_type
  16. from .rwbase import NotebookReader, NotebookWriter
  17. from .nbbase import new_code_cell, new_text_cell, new_worksheet, new_notebook
  18. #-----------------------------------------------------------------------------
  19. # Code
  20. #-----------------------------------------------------------------------------
  21. _encoding_declaration_re = re.compile(r"^#.*coding[:=]\s*([-\w.]+)")
  22. class PyReaderError(Exception):
  23. pass
  24. class PyReader(NotebookReader):
  25. def reads(self, s, **kwargs):
  26. return self.to_notebook(s,**kwargs)
  27. def to_notebook(self, s, **kwargs):
  28. lines = s.splitlines()
  29. cells = []
  30. cell_lines = []
  31. state = u'codecell'
  32. for line in lines:
  33. if line.startswith(u'# <nbformat>') or _encoding_declaration_re.match(line):
  34. pass
  35. elif line.startswith(u'# <codecell>'):
  36. cell = self.new_cell(state, cell_lines)
  37. if cell is not None:
  38. cells.append(cell)
  39. state = u'codecell'
  40. cell_lines = []
  41. elif line.startswith(u'# <htmlcell>'):
  42. cell = self.new_cell(state, cell_lines)
  43. if cell is not None:
  44. cells.append(cell)
  45. state = u'htmlcell'
  46. cell_lines = []
  47. elif line.startswith(u'# <markdowncell>'):
  48. cell = self.new_cell(state, cell_lines)
  49. if cell is not None:
  50. cells.append(cell)
  51. state = u'markdowncell'
  52. cell_lines = []
  53. else:
  54. cell_lines.append(line)
  55. if cell_lines and state == u'codecell':
  56. cell = self.new_cell(state, cell_lines)
  57. if cell is not None:
  58. cells.append(cell)
  59. ws = new_worksheet(cells=cells)
  60. nb = new_notebook(worksheets=[ws])
  61. return nb
  62. def new_cell(self, state, lines):
  63. if state == u'codecell':
  64. input = u'\n'.join(lines)
  65. input = input.strip(u'\n')
  66. if input:
  67. return new_code_cell(input=input)
  68. elif state == u'htmlcell':
  69. text = self._remove_comments(lines)
  70. if text:
  71. return new_text_cell(u'html',source=text)
  72. elif state == u'markdowncell':
  73. text = self._remove_comments(lines)
  74. if text:
  75. return new_text_cell(u'markdown',source=text)
  76. def _remove_comments(self, lines):
  77. new_lines = []
  78. for line in lines:
  79. if line.startswith(u'#'):
  80. new_lines.append(line[2:])
  81. else:
  82. new_lines.append(line)
  83. text = u'\n'.join(new_lines)
  84. text = text.strip(u'\n')
  85. return text
  86. def split_lines_into_blocks(self, lines):
  87. if len(lines) == 1:
  88. yield lines[0]
  89. raise StopIteration()
  90. import ast
  91. source = '\n'.join(lines)
  92. code = ast.parse(source)
  93. starts = [x.lineno-1 for x in code.body]
  94. for i in range(len(starts)-1):
  95. yield '\n'.join(lines[starts[i]:starts[i+1]]).strip('\n')
  96. yield '\n'.join(lines[starts[-1]:]).strip('\n')
  97. class PyWriter(NotebookWriter):
  98. def writes(self, nb, **kwargs):
  99. lines = [u'# -*- coding: utf-8 -*-']
  100. lines.extend([u'# <nbformat>2</nbformat>',''])
  101. for ws in nb.worksheets:
  102. for cell in ws.cells:
  103. if cell.cell_type == u'code':
  104. input = cell.get(u'input')
  105. if input is not None:
  106. lines.extend([u'# <codecell>',u''])
  107. lines.extend(input.splitlines())
  108. lines.append(u'')
  109. elif cell.cell_type == u'html':
  110. input = cell.get(u'source')
  111. if input is not None:
  112. lines.extend([u'# <htmlcell>',u''])
  113. lines.extend([u'# ' + line for line in input.splitlines()])
  114. lines.append(u'')
  115. elif cell.cell_type == u'markdown':
  116. input = cell.get(u'source')
  117. if input is not None:
  118. lines.extend([u'# <markdowncell>',u''])
  119. lines.extend([u'# ' + line for line in input.splitlines()])
  120. lines.append(u'')
  121. lines.append('')
  122. return unicode_type('\n'.join(lines))
  123. _reader = PyReader()
  124. _writer = PyWriter()
  125. reads = _reader.reads
  126. read = _reader.read
  127. to_notebook = _reader.to_notebook
  128. write = _writer.write
  129. writes = _writer.writes