Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/nbformat/v3/nbpy.py: 15%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

156 statements  

1"""Read and write notebooks as regular .py files. 

2 

3Authors: 

4 

5* Brian Granger 

6""" 

7 

8# ----------------------------------------------------------------------------- 

9# Copyright (C) 2008-2011 The IPython Development Team 

10# 

11# Distributed under the terms of the BSD License. The full license is in 

12# the file LICENSE, distributed as part of this software. 

13# ----------------------------------------------------------------------------- 

14 

15# ----------------------------------------------------------------------------- 

16# Imports 

17# ----------------------------------------------------------------------------- 

18from __future__ import annotations 

19 

20import ast 

21import re 

22 

23from .nbbase import ( 

24 nbformat, 

25 nbformat_minor, 

26 new_code_cell, 

27 new_heading_cell, 

28 new_notebook, 

29 new_text_cell, 

30 new_worksheet, 

31) 

32from .rwbase import NotebookReader, NotebookWriter 

33 

34# ----------------------------------------------------------------------------- 

35# Code 

36# ----------------------------------------------------------------------------- 

37 

38_encoding_declaration_re = re.compile(r"^#.*coding[:=]\s*([-\w.]+)") 

39 

40 

41class PyReaderError(Exception): 

42 """An error raised for a pyreader error.""" 

43 

44 

45class PyReader(NotebookReader): 

46 """A python notebook reader.""" 

47 

48 def reads(self, s, **kwargs): 

49 """Convert a string to a notebook""" 

50 return self.to_notebook(s, **kwargs) 

51 

52 def to_notebook(self, s, **kwargs): 

53 """Convert a string to a notebook""" 

54 lines = s.splitlines() 

55 cells = [] 

56 cell_lines: list[str] = [] 

57 kwargs = {} 

58 state = "codecell" 

59 for line in lines: 

60 if line.startswith("# <nbformat>") or _encoding_declaration_re.match(line): 

61 pass 

62 elif line.startswith("# <codecell>"): 

63 cell = self.new_cell(state, cell_lines, **kwargs) 

64 if cell is not None: 

65 cells.append(cell) 

66 state = "codecell" 

67 cell_lines = [] 

68 kwargs = {} 

69 elif line.startswith("# <htmlcell>"): 

70 cell = self.new_cell(state, cell_lines, **kwargs) 

71 if cell is not None: 

72 cells.append(cell) 

73 state = "htmlcell" 

74 cell_lines = [] 

75 kwargs = {} 

76 elif line.startswith("# <markdowncell>"): 

77 cell = self.new_cell(state, cell_lines, **kwargs) 

78 if cell is not None: 

79 cells.append(cell) 

80 state = "markdowncell" 

81 cell_lines = [] 

82 kwargs = {} 

83 # VERSIONHACK: plaintext -> raw 

84 elif line.startswith(("# <rawcell>", "# <plaintextcell>")): 

85 cell = self.new_cell(state, cell_lines, **kwargs) 

86 if cell is not None: 

87 cells.append(cell) 

88 state = "rawcell" 

89 cell_lines = [] 

90 kwargs = {} 

91 elif line.startswith("# <headingcell"): 

92 cell = self.new_cell(state, cell_lines, **kwargs) 

93 if cell is not None: 

94 cells.append(cell) 

95 cell_lines = [] 

96 m = re.match(r"# <headingcell level=(?P<level>\d)>", line) 

97 if m is not None: 

98 state = "headingcell" 

99 kwargs = {} 

100 kwargs["level"] = int(m.group("level")) 

101 else: 

102 state = "codecell" 

103 kwargs = {} 

104 cell_lines = [] 

105 else: 

106 cell_lines.append(line) 

107 if cell_lines and state == "codecell": 

108 cell = self.new_cell(state, cell_lines) 

109 if cell is not None: 

110 cells.append(cell) 

111 ws = new_worksheet(cells=cells) 

112 return new_notebook(worksheets=[ws]) 

113 

114 def new_cell(self, state, lines, **kwargs): 

115 """Create a new cell.""" 

116 if state == "codecell": 

117 input_ = "\n".join(lines) 

118 input_ = input_.strip("\n") 

119 if input_: 

120 return new_code_cell(input=input_) 

121 elif state == "htmlcell": 

122 text = self._remove_comments(lines) 

123 if text: 

124 return new_text_cell("html", source=text) 

125 elif state == "markdowncell": 

126 text = self._remove_comments(lines) 

127 if text: 

128 return new_text_cell("markdown", source=text) 

129 elif state == "rawcell": 

130 text = self._remove_comments(lines) 

131 if text: 

132 return new_text_cell("raw", source=text) 

133 elif state == "headingcell": 

134 text = self._remove_comments(lines) 

135 level = kwargs.get("level", 1) 

136 if text: 

137 return new_heading_cell(source=text, level=level) 

138 

139 def _remove_comments(self, lines): 

140 new_lines = [] 

141 for line in lines: 

142 if line.startswith("#"): 

143 new_lines.append(line[2:]) 

144 else: 

145 new_lines.append(line) 

146 text = "\n".join(new_lines) 

147 text = text.strip("\n") 

148 return text # noqa: RET504 

149 

150 def split_lines_into_blocks(self, lines): 

151 """Split lines into code blocks.""" 

152 if len(lines) == 1: 

153 yield lines[0] 

154 raise StopIteration() 

155 

156 source = "\n".join(lines) 

157 code = ast.parse(source) 

158 starts = [x.lineno - 1 for x in code.body] 

159 for i in range(len(starts) - 1): 

160 yield "\n".join(lines[starts[i] : starts[i + 1]]).strip("\n") 

161 yield "\n".join(lines[starts[-1] :]).strip("\n") 

162 

163 

164class PyWriter(NotebookWriter): 

165 """A Python notebook writer.""" 

166 

167 def writes(self, nb, **kwargs): 

168 """Convert a notebook to a string.""" 

169 lines = ["# -*- coding: utf-8 -*-"] 

170 lines.extend( 

171 [ 

172 "# <nbformat>%i.%i</nbformat>" % (nbformat, nbformat_minor), 

173 "", 

174 ] 

175 ) 

176 for ws in nb.worksheets: 

177 for cell in ws.cells: 

178 if cell.cell_type == "code": 

179 input_ = cell.get("input") 

180 if input_ is not None: 

181 lines.extend(["# <codecell>", ""]) 

182 lines.extend(input_.splitlines()) 

183 lines.append("") 

184 elif cell.cell_type == "html": 

185 input_ = cell.get("source") 

186 if input_ is not None: 

187 lines.extend(["# <htmlcell>", ""]) 

188 lines.extend(["# " + line for line in input_.splitlines()]) 

189 lines.append("") 

190 elif cell.cell_type == "markdown": 

191 input_ = cell.get("source") 

192 if input_ is not None: 

193 lines.extend(["# <markdowncell>", ""]) 

194 lines.extend(["# " + line for line in input_.splitlines()]) 

195 lines.append("") 

196 elif cell.cell_type == "raw": 

197 input_ = cell.get("source") 

198 if input_ is not None: 

199 lines.extend(["# <rawcell>", ""]) 

200 lines.extend(["# " + line for line in input_.splitlines()]) 

201 lines.append("") 

202 elif cell.cell_type == "heading": 

203 input_ = cell.get("source") 

204 level = cell.get("level", 1) 

205 if input_ is not None: 

206 lines.extend(["# <headingcell level=%s>" % level, ""]) 

207 lines.extend(["# " + line for line in input_.splitlines()]) 

208 lines.append("") 

209 lines.append("") 

210 return "\n".join(lines) 

211 

212 

213_reader = PyReader() 

214_writer = PyWriter() 

215 

216reads = _reader.reads 

217read = _reader.read 

218to_notebook = _reader.to_notebook 

219write = _writer.write 

220writes = _writer.writes