Coverage for pymend\docstring_parser\common.py: 100%

103 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2024-04-20 19:09 +0200

1"""Common methods for parsing.""" 

2 

3import enum 

4from dataclasses import dataclass 

5from typing import Optional, Union 

6 

7from typing_extensions import TypeAlias 

8 

9PARAM_KEYWORDS = { 

10 "param", 

11 "parameter", 

12 "arg", 

13 "argument", 

14 "attribute", 

15 "key", 

16 "keyword", 

17} 

18# These could be made into frozen sets 

19# Then one could create a dictionary 

20# {KEYWORD: FUNCTION_THAT_HANDLES_THAT_KEYWORD_SECTION} # noqa: ERA001 

21RAISES_KEYWORDS = {"raises", "raise", "except", "exception"} 

22DEPRECATION_KEYWORDS = {"deprecation", "deprecated"} 

23RETURNS_KEYWORDS = {"return", "returns"} 

24YIELDS_KEYWORDS = {"yield", "yields"} 

25EXAMPLES_KEYWORDS = {"example", "examples"} 

26 

27 

28def clean_str(string: str) -> Optional[str]: 

29 """Strip a string and return None if it is now empty. 

30 

31 Parameters 

32 ---------- 

33 string : str 

34 String to clean 

35 

36 Returns 

37 ------- 

38 Optional[str] 

39 None of the stripped string is empty. Otherwise the stripped string. 

40 """ 

41 string = string.strip() 

42 return string if string != "" else None 

43 

44 

45class ParseError(RuntimeError): 

46 """Base class for all parsing related errors.""" 

47 

48 

49class DocstringStyle(enum.Enum): 

50 """Docstring style.""" 

51 

52 REST = 1 

53 GOOGLE = 2 

54 NUMPYDOC = 3 

55 EPYDOC = 4 

56 AUTO = 255 

57 

58 

59class RenderingStyle(enum.Enum): 

60 """Rendering style when unparsing parsed docstrings.""" 

61 

62 COMPACT = 1 

63 CLEAN = 2 

64 EXPANDED = 3 

65 

66 

67@dataclass 

68class DocstringMeta: 

69 """Docstring meta information. 

70 

71 Symbolizes lines in form of 

72 

73 Parameters 

74 ---------- 

75 args : List[str] 

76 list of arguments. The exact content of this variable is 

77 dependent on the kind of docstring; it's used to distinguish 

78 between custom docstring meta information items. 

79 description : Optional[str] 

80 associated docstring description. 

81 """ 

82 

83 args: list[str] 

84 description: Optional[str] 

85 

86 

87@dataclass 

88class DocstringParam(DocstringMeta): 

89 """DocstringMeta symbolizing :param metadata.""" 

90 

91 arg_name: str 

92 type_name: Optional[str] 

93 is_optional: Optional[bool] 

94 default: Optional[str] 

95 

96 

97@dataclass 

98class DocstringReturns(DocstringMeta): 

99 """DocstringMeta symbolizing :returns metadata.""" 

100 

101 type_name: Optional[str] 

102 is_generator: bool 

103 return_name: Optional[str] = None 

104 

105 

106@dataclass 

107class DocstringYields(DocstringMeta): 

108 """DocstringMeta symbolizing :yields metadata.""" 

109 

110 type_name: Optional[str] 

111 is_generator: bool 

112 yield_name: Optional[str] = None 

113 

114 

115@dataclass 

116class DocstringRaises(DocstringMeta): 

117 """DocstringMeta symbolizing :raises metadata.""" 

118 

119 type_name: Optional[str] 

120 

121 

122MainSections: TypeAlias = Union[ 

123 DocstringParam, DocstringRaises, DocstringReturns, DocstringYields 

124] 

125 

126 

127@dataclass 

128class DocstringDeprecated(DocstringMeta): 

129 """DocstringMeta symbolizing deprecation metadata.""" 

130 

131 version: Optional[str] 

132 

133 

134@dataclass 

135class DocstringExample(DocstringMeta): 

136 """DocstringMeta symbolizing example metadata.""" 

137 

138 snippet: Optional[str] 

139 

140 

141class Docstring: 

142 """Docstring object representation.""" 

143 

144 def __init__( 

145 self, 

146 style: Optional[DocstringStyle] = None, 

147 ) -> None: 

148 """Initialize self. 

149 

150 Parameters 

151 ---------- 

152 style : Optional[DocstringStyle] 

153 Style that this docstring was formatted in. (Default value = None) 

154 """ 

155 self.short_description: Optional[str] = None 

156 self.long_description: Optional[str] = None 

157 self.blank_after_short_description: bool = False 

158 self.blank_after_long_description: bool = False 

159 self.meta: list[DocstringMeta] = [] 

160 self.style: Optional[DocstringStyle] = style 

161 

162 @property 

163 def params(self) -> list[DocstringParam]: 

164 """Return a list of information on function params. 

165 

166 Returns 

167 ------- 

168 list[DocstringParam] 

169 list of information on function params 

170 """ 

171 return [item for item in self.meta if isinstance(item, DocstringParam)] 

172 

173 @property 

174 def raises(self) -> list[DocstringRaises]: 

175 """Return a list of the exceptions that the function may raise. 

176 

177 Returns 

178 ------- 

179 list[DocstringRaises] 

180 list of the exceptions that the function may raise. 

181 """ 

182 return [item for item in self.meta if isinstance(item, DocstringRaises)] 

183 

184 @property 

185 def returns(self) -> Optional[DocstringReturns]: 

186 """Return a single information on function return. 

187 

188 Takes the first return information. 

189 

190 Returns 

191 ------- 

192 Optional[DocstringReturns] 

193 Single information on function return. 

194 """ 

195 return next( 

196 (item for item in self.meta if isinstance(item, DocstringReturns)), 

197 None, 

198 ) 

199 

200 @property 

201 def many_returns(self) -> list[DocstringReturns]: 

202 """Return a list of information on function return. 

203 

204 Returns 

205 ------- 

206 list[DocstringReturns] 

207 list of information on function return. 

208 """ 

209 return [item for item in self.meta if isinstance(item, DocstringReturns)] 

210 

211 @property 

212 def yields(self) -> Optional[DocstringYields]: 

213 """Return information on function yield. 

214 

215 Takes the first generator information. 

216 

217 Returns 

218 ------- 

219 Optional[DocstringYields] 

220 Single information on function yield. 

221 """ 

222 return next( 

223 ( 

224 item 

225 for item in self.meta 

226 if isinstance(item, DocstringYields) and item.is_generator 

227 ), 

228 None, 

229 ) 

230 

231 @property 

232 def many_yields(self) -> list[DocstringYields]: 

233 """Return a list of information on function yields. 

234 

235 Returns 

236 ------- 

237 list[DocstringYields] 

238 list of information on function yields. 

239 """ 

240 return [item for item in self.meta if isinstance(item, DocstringYields)] 

241 

242 @property 

243 def deprecation(self) -> Optional[DocstringDeprecated]: 

244 """Return a single information on function deprecation notes. 

245 

246 Returns 

247 ------- 

248 Optional[DocstringDeprecated] 

249 single information on function deprecation notes. 

250 """ 

251 return next( 

252 (item for item in self.meta if isinstance(item, DocstringDeprecated)), 

253 None, 

254 ) 

255 

256 @property 

257 def examples(self) -> list[DocstringExample]: 

258 """Return a list of information on function examples. 

259 

260 Returns 

261 ------- 

262 list[DocstringExample] 

263 list of information on function examples. 

264 """ 

265 return [item for item in self.meta if isinstance(item, DocstringExample)] 

266 

267 

268def split_description(docstring: Docstring, desc_chunk: str) -> None: 

269 """Break description into short and long parts. 

270 

271 Parameters 

272 ---------- 

273 docstring : Docstring 

274 Docstring to fill with description information. 

275 desc_chunk : str 

276 Chunk of the raw docstring representing the description. 

277 """ 

278 parts = desc_chunk.split("\n", 1) 

279 docstring.short_description = parts[0] or None 

280 if len(parts) > 1: 

281 long_desc_chunk = parts[1] or "" 

282 docstring.blank_after_short_description = long_desc_chunk.startswith("\n") 

283 docstring.blank_after_long_description = long_desc_chunk.endswith("\n\n") 

284 docstring.long_description = long_desc_chunk.strip() or None 

285 

286 

287def append_description(docstring: Docstring, parts: list[str]) -> None: 

288 """Append the docstrings description to the output stream. 

289 

290 Parameters 

291 ---------- 

292 docstring : Docstring 

293 Docstring whose information should be added. 

294 parts : list[str] 

295 List of strings representing the output of compose(). 

296 Descriptions should be added to this. 

297 """ 

298 if docstring.short_description: 

299 parts.append(docstring.short_description) 

300 if docstring.blank_after_short_description: 

301 parts.append("") 

302 if docstring.long_description: 

303 parts.append(docstring.long_description) 

304 if docstring.blank_after_long_description: 

305 parts.append("")