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
« prev ^ index » next coverage.py v7.3.2, created at 2024-04-20 19:09 +0200
1"""Common methods for parsing."""
3import enum
4from dataclasses import dataclass
5from typing import Optional, Union
7from typing_extensions import TypeAlias
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"}
28def clean_str(string: str) -> Optional[str]:
29 """Strip a string and return None if it is now empty.
31 Parameters
32 ----------
33 string : str
34 String to clean
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
45class ParseError(RuntimeError):
46 """Base class for all parsing related errors."""
49class DocstringStyle(enum.Enum):
50 """Docstring style."""
52 REST = 1
53 GOOGLE = 2
54 NUMPYDOC = 3
55 EPYDOC = 4
56 AUTO = 255
59class RenderingStyle(enum.Enum):
60 """Rendering style when unparsing parsed docstrings."""
62 COMPACT = 1
63 CLEAN = 2
64 EXPANDED = 3
67@dataclass
68class DocstringMeta:
69 """Docstring meta information.
71 Symbolizes lines in form of
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 """
83 args: list[str]
84 description: Optional[str]
87@dataclass
88class DocstringParam(DocstringMeta):
89 """DocstringMeta symbolizing :param metadata."""
91 arg_name: str
92 type_name: Optional[str]
93 is_optional: Optional[bool]
94 default: Optional[str]
97@dataclass
98class DocstringReturns(DocstringMeta):
99 """DocstringMeta symbolizing :returns metadata."""
101 type_name: Optional[str]
102 is_generator: bool
103 return_name: Optional[str] = None
106@dataclass
107class DocstringYields(DocstringMeta):
108 """DocstringMeta symbolizing :yields metadata."""
110 type_name: Optional[str]
111 is_generator: bool
112 yield_name: Optional[str] = None
115@dataclass
116class DocstringRaises(DocstringMeta):
117 """DocstringMeta symbolizing :raises metadata."""
119 type_name: Optional[str]
122MainSections: TypeAlias = Union[
123 DocstringParam, DocstringRaises, DocstringReturns, DocstringYields
124]
127@dataclass
128class DocstringDeprecated(DocstringMeta):
129 """DocstringMeta symbolizing deprecation metadata."""
131 version: Optional[str]
134@dataclass
135class DocstringExample(DocstringMeta):
136 """DocstringMeta symbolizing example metadata."""
138 snippet: Optional[str]
141class Docstring:
142 """Docstring object representation."""
144 def __init__(
145 self,
146 style: Optional[DocstringStyle] = None,
147 ) -> None:
148 """Initialize self.
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
162 @property
163 def params(self) -> list[DocstringParam]:
164 """Return a list of information on function params.
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)]
173 @property
174 def raises(self) -> list[DocstringRaises]:
175 """Return a list of the exceptions that the function may raise.
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)]
184 @property
185 def returns(self) -> Optional[DocstringReturns]:
186 """Return a single information on function return.
188 Takes the first return information.
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 )
200 @property
201 def many_returns(self) -> list[DocstringReturns]:
202 """Return a list of information on function return.
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)]
211 @property
212 def yields(self) -> Optional[DocstringYields]:
213 """Return information on function yield.
215 Takes the first generator information.
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 )
231 @property
232 def many_yields(self) -> list[DocstringYields]:
233 """Return a list of information on function yields.
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)]
242 @property
243 def deprecation(self) -> Optional[DocstringDeprecated]:
244 """Return a single information on function deprecation notes.
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 )
256 @property
257 def examples(self) -> list[DocstringExample]:
258 """Return a list of information on function examples.
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)]
268def split_description(docstring: Docstring, desc_chunk: str) -> None:
269 """Break description into short and long parts.
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
287def append_description(docstring: Docstring, parts: list[str]) -> None:
288 """Append the docstrings description to the output stream.
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("")