singlestoredb 1.16.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. singlestoredb/__init__.py +75 -0
  2. singlestoredb/ai/__init__.py +2 -0
  3. singlestoredb/ai/chat.py +139 -0
  4. singlestoredb/ai/embeddings.py +128 -0
  5. singlestoredb/alchemy/__init__.py +90 -0
  6. singlestoredb/apps/__init__.py +3 -0
  7. singlestoredb/apps/_cloud_functions.py +90 -0
  8. singlestoredb/apps/_config.py +72 -0
  9. singlestoredb/apps/_connection_info.py +18 -0
  10. singlestoredb/apps/_dashboards.py +47 -0
  11. singlestoredb/apps/_process.py +32 -0
  12. singlestoredb/apps/_python_udfs.py +100 -0
  13. singlestoredb/apps/_stdout_supress.py +30 -0
  14. singlestoredb/apps/_uvicorn_util.py +36 -0
  15. singlestoredb/auth.py +245 -0
  16. singlestoredb/config.py +484 -0
  17. singlestoredb/connection.py +1487 -0
  18. singlestoredb/converters.py +950 -0
  19. singlestoredb/docstring/__init__.py +33 -0
  20. singlestoredb/docstring/attrdoc.py +126 -0
  21. singlestoredb/docstring/common.py +230 -0
  22. singlestoredb/docstring/epydoc.py +267 -0
  23. singlestoredb/docstring/google.py +412 -0
  24. singlestoredb/docstring/numpydoc.py +562 -0
  25. singlestoredb/docstring/parser.py +100 -0
  26. singlestoredb/docstring/py.typed +1 -0
  27. singlestoredb/docstring/rest.py +256 -0
  28. singlestoredb/docstring/tests/__init__.py +1 -0
  29. singlestoredb/docstring/tests/_pydoctor.py +21 -0
  30. singlestoredb/docstring/tests/test_epydoc.py +729 -0
  31. singlestoredb/docstring/tests/test_google.py +1007 -0
  32. singlestoredb/docstring/tests/test_numpydoc.py +1100 -0
  33. singlestoredb/docstring/tests/test_parse_from_object.py +109 -0
  34. singlestoredb/docstring/tests/test_parser.py +248 -0
  35. singlestoredb/docstring/tests/test_rest.py +547 -0
  36. singlestoredb/docstring/tests/test_util.py +70 -0
  37. singlestoredb/docstring/util.py +141 -0
  38. singlestoredb/exceptions.py +120 -0
  39. singlestoredb/functions/__init__.py +16 -0
  40. singlestoredb/functions/decorator.py +201 -0
  41. singlestoredb/functions/dtypes.py +1793 -0
  42. singlestoredb/functions/ext/__init__.py +1 -0
  43. singlestoredb/functions/ext/arrow.py +375 -0
  44. singlestoredb/functions/ext/asgi.py +2133 -0
  45. singlestoredb/functions/ext/json.py +420 -0
  46. singlestoredb/functions/ext/mmap.py +413 -0
  47. singlestoredb/functions/ext/rowdat_1.py +724 -0
  48. singlestoredb/functions/ext/timer.py +89 -0
  49. singlestoredb/functions/ext/utils.py +218 -0
  50. singlestoredb/functions/signature.py +1578 -0
  51. singlestoredb/functions/typing/__init__.py +41 -0
  52. singlestoredb/functions/typing/numpy.py +20 -0
  53. singlestoredb/functions/typing/pandas.py +2 -0
  54. singlestoredb/functions/typing/polars.py +2 -0
  55. singlestoredb/functions/typing/pyarrow.py +2 -0
  56. singlestoredb/functions/utils.py +421 -0
  57. singlestoredb/fusion/__init__.py +11 -0
  58. singlestoredb/fusion/graphql.py +213 -0
  59. singlestoredb/fusion/handler.py +916 -0
  60. singlestoredb/fusion/handlers/__init__.py +0 -0
  61. singlestoredb/fusion/handlers/export.py +525 -0
  62. singlestoredb/fusion/handlers/files.py +690 -0
  63. singlestoredb/fusion/handlers/job.py +660 -0
  64. singlestoredb/fusion/handlers/models.py +250 -0
  65. singlestoredb/fusion/handlers/stage.py +502 -0
  66. singlestoredb/fusion/handlers/utils.py +324 -0
  67. singlestoredb/fusion/handlers/workspace.py +956 -0
  68. singlestoredb/fusion/registry.py +249 -0
  69. singlestoredb/fusion/result.py +399 -0
  70. singlestoredb/http/__init__.py +27 -0
  71. singlestoredb/http/connection.py +1267 -0
  72. singlestoredb/magics/__init__.py +34 -0
  73. singlestoredb/magics/run_personal.py +137 -0
  74. singlestoredb/magics/run_shared.py +134 -0
  75. singlestoredb/management/__init__.py +9 -0
  76. singlestoredb/management/billing_usage.py +148 -0
  77. singlestoredb/management/cluster.py +462 -0
  78. singlestoredb/management/export.py +295 -0
  79. singlestoredb/management/files.py +1102 -0
  80. singlestoredb/management/inference_api.py +105 -0
  81. singlestoredb/management/job.py +887 -0
  82. singlestoredb/management/manager.py +373 -0
  83. singlestoredb/management/organization.py +226 -0
  84. singlestoredb/management/region.py +169 -0
  85. singlestoredb/management/utils.py +423 -0
  86. singlestoredb/management/workspace.py +1927 -0
  87. singlestoredb/mysql/__init__.py +177 -0
  88. singlestoredb/mysql/_auth.py +298 -0
  89. singlestoredb/mysql/charset.py +214 -0
  90. singlestoredb/mysql/connection.py +2032 -0
  91. singlestoredb/mysql/constants/CLIENT.py +38 -0
  92. singlestoredb/mysql/constants/COMMAND.py +32 -0
  93. singlestoredb/mysql/constants/CR.py +78 -0
  94. singlestoredb/mysql/constants/ER.py +474 -0
  95. singlestoredb/mysql/constants/EXTENDED_TYPE.py +3 -0
  96. singlestoredb/mysql/constants/FIELD_TYPE.py +48 -0
  97. singlestoredb/mysql/constants/FLAG.py +15 -0
  98. singlestoredb/mysql/constants/SERVER_STATUS.py +10 -0
  99. singlestoredb/mysql/constants/VECTOR_TYPE.py +6 -0
  100. singlestoredb/mysql/constants/__init__.py +0 -0
  101. singlestoredb/mysql/converters.py +271 -0
  102. singlestoredb/mysql/cursors.py +896 -0
  103. singlestoredb/mysql/err.py +92 -0
  104. singlestoredb/mysql/optionfile.py +20 -0
  105. singlestoredb/mysql/protocol.py +450 -0
  106. singlestoredb/mysql/tests/__init__.py +19 -0
  107. singlestoredb/mysql/tests/base.py +126 -0
  108. singlestoredb/mysql/tests/conftest.py +37 -0
  109. singlestoredb/mysql/tests/test_DictCursor.py +132 -0
  110. singlestoredb/mysql/tests/test_SSCursor.py +141 -0
  111. singlestoredb/mysql/tests/test_basic.py +452 -0
  112. singlestoredb/mysql/tests/test_connection.py +851 -0
  113. singlestoredb/mysql/tests/test_converters.py +58 -0
  114. singlestoredb/mysql/tests/test_cursor.py +141 -0
  115. singlestoredb/mysql/tests/test_err.py +16 -0
  116. singlestoredb/mysql/tests/test_issues.py +514 -0
  117. singlestoredb/mysql/tests/test_load_local.py +75 -0
  118. singlestoredb/mysql/tests/test_nextset.py +88 -0
  119. singlestoredb/mysql/tests/test_optionfile.py +27 -0
  120. singlestoredb/mysql/tests/thirdparty/__init__.py +6 -0
  121. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/__init__.py +9 -0
  122. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/capabilities.py +323 -0
  123. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/dbapi20.py +865 -0
  124. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_capabilities.py +110 -0
  125. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_dbapi20.py +224 -0
  126. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_nonstandard.py +101 -0
  127. singlestoredb/mysql/times.py +23 -0
  128. singlestoredb/notebook/__init__.py +16 -0
  129. singlestoredb/notebook/_objects.py +213 -0
  130. singlestoredb/notebook/_portal.py +352 -0
  131. singlestoredb/py.typed +0 -0
  132. singlestoredb/pytest.py +352 -0
  133. singlestoredb/server/__init__.py +0 -0
  134. singlestoredb/server/docker.py +452 -0
  135. singlestoredb/server/free_tier.py +267 -0
  136. singlestoredb/tests/__init__.py +0 -0
  137. singlestoredb/tests/alltypes.sql +307 -0
  138. singlestoredb/tests/alltypes_no_nulls.sql +208 -0
  139. singlestoredb/tests/empty.sql +0 -0
  140. singlestoredb/tests/ext_funcs/__init__.py +702 -0
  141. singlestoredb/tests/local_infile.csv +3 -0
  142. singlestoredb/tests/test.ipynb +18 -0
  143. singlestoredb/tests/test.sql +680 -0
  144. singlestoredb/tests/test2.ipynb +18 -0
  145. singlestoredb/tests/test2.sql +1 -0
  146. singlestoredb/tests/test_basics.py +1332 -0
  147. singlestoredb/tests/test_config.py +318 -0
  148. singlestoredb/tests/test_connection.py +3103 -0
  149. singlestoredb/tests/test_dbapi.py +27 -0
  150. singlestoredb/tests/test_exceptions.py +45 -0
  151. singlestoredb/tests/test_ext_func.py +1472 -0
  152. singlestoredb/tests/test_ext_func_data.py +1101 -0
  153. singlestoredb/tests/test_fusion.py +1527 -0
  154. singlestoredb/tests/test_http.py +288 -0
  155. singlestoredb/tests/test_management.py +1599 -0
  156. singlestoredb/tests/test_plugin.py +33 -0
  157. singlestoredb/tests/test_results.py +171 -0
  158. singlestoredb/tests/test_types.py +132 -0
  159. singlestoredb/tests/test_udf.py +737 -0
  160. singlestoredb/tests/test_udf_returns.py +459 -0
  161. singlestoredb/tests/test_vectorstore.py +51 -0
  162. singlestoredb/tests/test_xdict.py +333 -0
  163. singlestoredb/tests/utils.py +141 -0
  164. singlestoredb/types.py +373 -0
  165. singlestoredb/utils/__init__.py +0 -0
  166. singlestoredb/utils/config.py +950 -0
  167. singlestoredb/utils/convert_rows.py +69 -0
  168. singlestoredb/utils/debug.py +13 -0
  169. singlestoredb/utils/dtypes.py +205 -0
  170. singlestoredb/utils/events.py +65 -0
  171. singlestoredb/utils/mogrify.py +151 -0
  172. singlestoredb/utils/results.py +585 -0
  173. singlestoredb/utils/xdict.py +425 -0
  174. singlestoredb/vectorstore.py +192 -0
  175. singlestoredb/warnings.py +5 -0
  176. singlestoredb-1.16.1.dist-info/METADATA +165 -0
  177. singlestoredb-1.16.1.dist-info/RECORD +183 -0
  178. singlestoredb-1.16.1.dist-info/WHEEL +5 -0
  179. singlestoredb-1.16.1.dist-info/entry_points.txt +2 -0
  180. singlestoredb-1.16.1.dist-info/licenses/LICENSE +201 -0
  181. singlestoredb-1.16.1.dist-info/top_level.txt +3 -0
  182. sqlx/__init__.py +4 -0
  183. sqlx/magic.py +113 -0
@@ -0,0 +1,412 @@
1
+ """Google-style docstring parsing."""
2
+ import inspect
3
+ import re
4
+ import typing as T
5
+ from collections import namedtuple
6
+ from collections import OrderedDict
7
+ from enum import IntEnum
8
+
9
+ from .common import Docstring
10
+ from .common import DocstringExample
11
+ from .common import DocstringMeta
12
+ from .common import DocstringParam
13
+ from .common import DocstringRaises
14
+ from .common import DocstringReturns
15
+ from .common import DocstringStyle
16
+ from .common import EXAMPLES_KEYWORDS
17
+ from .common import PARAM_KEYWORDS
18
+ from .common import ParseError
19
+ from .common import RAISES_KEYWORDS
20
+ from .common import RenderingStyle
21
+ from .common import RETURNS_KEYWORDS
22
+ from .common import YIELDS_KEYWORDS
23
+
24
+
25
+ class SectionType(IntEnum):
26
+ """Types of sections."""
27
+
28
+ SINGULAR = 0
29
+ """For sections like examples."""
30
+
31
+ MULTIPLE = 1
32
+ """For sections like params."""
33
+
34
+ SINGULAR_OR_MULTIPLE = 2
35
+ """For sections like returns or yields."""
36
+
37
+
38
+ class Section(namedtuple('SectionBase', 'title key type')):
39
+ """A docstring section."""
40
+
41
+
42
+ GOOGLE_TYPED_ARG_REGEX = re.compile(r'\s*(.+?)\s*\(\s*(.*[^\s]+)\s*\)')
43
+ GOOGLE_ARG_DESC_REGEX = re.compile(r'.*\. Defaults to (.+)\.')
44
+ MULTIPLE_PATTERN = re.compile(r'(\s*[^:\s]+:)|([^:]*\]:.*)')
45
+
46
+ DEFAULT_SECTIONS = [
47
+ Section('Arguments', 'param', SectionType.MULTIPLE),
48
+ Section('Args', 'param', SectionType.MULTIPLE),
49
+ Section('Parameters', 'param', SectionType.MULTIPLE),
50
+ Section('Params', 'param', SectionType.MULTIPLE),
51
+ Section('Raises', 'raises', SectionType.MULTIPLE),
52
+ Section('Exceptions', 'raises', SectionType.MULTIPLE),
53
+ Section('Except', 'raises', SectionType.MULTIPLE),
54
+ Section('Attributes', 'attribute', SectionType.MULTIPLE),
55
+ Section('Example', 'examples', SectionType.SINGULAR),
56
+ Section('Examples', 'examples', SectionType.SINGULAR),
57
+ Section('Returns', 'returns', SectionType.SINGULAR_OR_MULTIPLE),
58
+ Section('Yields', 'yields', SectionType.SINGULAR_OR_MULTIPLE),
59
+ ]
60
+
61
+
62
+ class GoogleParser:
63
+ """Parser for Google-style docstrings."""
64
+
65
+ def __init__(
66
+ self, sections: T.Optional[T.List[Section]] = None, title_colon: bool = True,
67
+ ):
68
+ """Setup sections.
69
+
70
+ :param sections: Recognized sections or None to defaults.
71
+ :param title_colon: require colon after section title.
72
+ """
73
+ if not sections:
74
+ sections = DEFAULT_SECTIONS
75
+ self.sections = {s.title: s for s in sections}
76
+ self.title_colon = title_colon
77
+ self._setup()
78
+
79
+ def _setup(self) -> None:
80
+ if self.title_colon:
81
+ colon = ':'
82
+ else:
83
+ colon = ''
84
+ self.titles_re = re.compile(
85
+ '^('
86
+ + '|'.join(f'({t})' for t in self.sections)
87
+ + ')'
88
+ + colon
89
+ + '[ \t\r\f\v]*$',
90
+ flags=re.M,
91
+ )
92
+
93
+ def _build_meta(self, text: str, title: str) -> DocstringMeta:
94
+ """Build docstring element.
95
+
96
+ :param text: docstring element text
97
+ :param title: title of section containing element
98
+ :return:
99
+ """
100
+
101
+ section = self.sections[title]
102
+
103
+ if (
104
+ section.type == SectionType.SINGULAR_OR_MULTIPLE
105
+ and not MULTIPLE_PATTERN.match(text)
106
+ ) or section.type == SectionType.SINGULAR:
107
+ return self._build_single_meta(section, text)
108
+
109
+ if ':' not in text:
110
+ raise ParseError(f'Expected a colon in {text!r}.')
111
+
112
+ # Split spec and description
113
+ before, desc = text.split(':', 1)
114
+
115
+ if before and '\n' in before:
116
+ # If there is a newline in the first line, clean it up
117
+ first_line, rest = before.split('\n', 1)
118
+ before = first_line + inspect.cleandoc(rest)
119
+
120
+ if desc:
121
+ desc = desc[1:] if desc[0] == ' ' else desc
122
+ if '\n' in desc:
123
+ first_line, rest = desc.split('\n', 1)
124
+ desc = first_line + '\n' + inspect.cleandoc(rest)
125
+ desc = desc.strip('\n')
126
+
127
+ return self._build_multi_meta(section, before, desc)
128
+
129
+ @staticmethod
130
+ def _build_single_meta(section: Section, desc: str) -> DocstringMeta:
131
+ if section.key in RETURNS_KEYWORDS | YIELDS_KEYWORDS:
132
+ return DocstringReturns(
133
+ args=[section.key],
134
+ description=desc,
135
+ type_name=None,
136
+ is_generator=section.key in YIELDS_KEYWORDS,
137
+ )
138
+ if section.key in RAISES_KEYWORDS:
139
+ return DocstringRaises(
140
+ args=[section.key], description=desc, type_name=None,
141
+ )
142
+ if section.key in EXAMPLES_KEYWORDS:
143
+ return DocstringExample(
144
+ args=[section.key], snippet=None, description=desc,
145
+ )
146
+ if section.key in PARAM_KEYWORDS:
147
+ raise ParseError('Expected paramenter name.')
148
+ return DocstringMeta(args=[section.key], description=desc)
149
+
150
+ @staticmethod
151
+ def _build_multi_meta(
152
+ section: Section, before: str, desc: str,
153
+ ) -> DocstringMeta:
154
+ if section.key in PARAM_KEYWORDS:
155
+ match = GOOGLE_TYPED_ARG_REGEX.match(before)
156
+ if match:
157
+ arg_name, type_name = match.group(1, 2)
158
+ if type_name.endswith(', optional'):
159
+ is_optional = True
160
+ type_name = type_name[:-10]
161
+ elif type_name.endswith('?'):
162
+ is_optional = True
163
+ type_name = type_name[:-1]
164
+ else:
165
+ is_optional = False
166
+ else:
167
+ arg_name, type_name = before, None
168
+ is_optional = None
169
+
170
+ match = GOOGLE_ARG_DESC_REGEX.match(desc)
171
+ default = match.group(1) if match else None
172
+
173
+ return DocstringParam(
174
+ args=[section.key, before],
175
+ description=desc,
176
+ arg_name=arg_name,
177
+ type_name=type_name,
178
+ is_optional=is_optional,
179
+ default=default,
180
+ )
181
+ if section.key in RETURNS_KEYWORDS | YIELDS_KEYWORDS:
182
+ return DocstringReturns(
183
+ args=[section.key, before],
184
+ description=desc,
185
+ type_name=before,
186
+ is_generator=section.key in YIELDS_KEYWORDS,
187
+ )
188
+ if section.key in RAISES_KEYWORDS:
189
+ return DocstringRaises(
190
+ args=[section.key, before], description=desc, type_name=before,
191
+ )
192
+ return DocstringMeta(args=[section.key, before], description=desc)
193
+
194
+ def add_section(self, section: Section) -> None:
195
+ """Add or replace a section.
196
+
197
+ :param section: The new section.
198
+ """
199
+
200
+ self.sections[section.title] = section
201
+ self._setup()
202
+
203
+ def parse(self, text: T.Optional[str]) -> Docstring:
204
+ """Parse the Google-style docstring into its components.
205
+
206
+ :returns: parsed docstring
207
+ """
208
+ ret = Docstring(style=DocstringStyle.GOOGLE)
209
+ if not text:
210
+ return ret
211
+
212
+ # Clean according to PEP-0257
213
+ text = inspect.cleandoc(text)
214
+
215
+ # Find first title and split on its position
216
+ match = self.titles_re.search(text)
217
+ if match:
218
+ desc_chunk = text[:match.start()]
219
+ meta_chunk = text[match.start():]
220
+ else:
221
+ desc_chunk = text
222
+ meta_chunk = ''
223
+
224
+ # Break description into short and long parts
225
+ parts = desc_chunk.split('\n', 1)
226
+ ret.short_description = parts[0] or None
227
+ if len(parts) > 1:
228
+ long_desc_chunk = parts[1] or ''
229
+ ret.blank_after_short_description = long_desc_chunk.startswith(
230
+ '\n',
231
+ )
232
+ ret.blank_after_long_description = long_desc_chunk.endswith('\n\n')
233
+ ret.long_description = long_desc_chunk.strip() or None
234
+
235
+ # Split by sections determined by titles
236
+ matches = list(self.titles_re.finditer(meta_chunk))
237
+ if not matches:
238
+ return ret
239
+ splits = []
240
+ for j in range(len(matches) - 1):
241
+ splits.append((matches[j].end(), matches[j + 1].start()))
242
+ splits.append((matches[-1].end(), len(meta_chunk)))
243
+
244
+ chunks = OrderedDict() # type: T.MutableMapping[str,str]
245
+ for j, (start, end) in enumerate(splits):
246
+ title = matches[j].group(1)
247
+ if title not in self.sections:
248
+ continue
249
+
250
+ # Clear Any Unknown Meta
251
+ # Ref: https://github.com/rr-/docstring_parser/issues/29
252
+ meta_details = meta_chunk[start:end]
253
+ unknown_meta = re.search(r'\n\S', meta_details)
254
+ if unknown_meta is not None:
255
+ meta_details = meta_details[: unknown_meta.start()]
256
+
257
+ chunks[title] = meta_details.strip('\n')
258
+ if not chunks:
259
+ return ret
260
+
261
+ # Add elements from each chunk
262
+ for title, chunk in chunks.items():
263
+ # Determine indent
264
+ indent_match = re.search(r'^\s*', chunk)
265
+ if not indent_match:
266
+ raise ParseError(f'Can\'t infer indent from "{chunk}"')
267
+ indent = indent_match.group()
268
+
269
+ # Check for singular elements
270
+ if self.sections[title].type in [
271
+ SectionType.SINGULAR,
272
+ SectionType.SINGULAR_OR_MULTIPLE,
273
+ ]:
274
+ part = inspect.cleandoc(chunk)
275
+ ret.meta.append(self._build_meta(part, title))
276
+ continue
277
+
278
+ # Split based on lines which have exactly that indent
279
+ _re = '^' + indent + r'(?=\S)'
280
+ c_matches = list(re.finditer(_re, chunk, flags=re.M))
281
+ if not c_matches:
282
+ raise ParseError(f'No specification for "{title}": "{chunk}"')
283
+ c_splits = []
284
+ for j in range(len(c_matches) - 1):
285
+ c_splits.append((c_matches[j].end(), c_matches[j + 1].start()))
286
+ c_splits.append((c_matches[-1].end(), len(chunk)))
287
+ for j, (start, end) in enumerate(c_splits):
288
+ part = chunk[start:end].strip('\n')
289
+ ret.meta.append(self._build_meta(part, title))
290
+
291
+ return ret
292
+
293
+
294
+ def parse(text: T.Optional[str]) -> Docstring:
295
+ """Parse the Google-style docstring into its components.
296
+
297
+ :returns: parsed docstring
298
+ """
299
+ return GoogleParser().parse(text)
300
+
301
+
302
+ def compose(
303
+ docstring: Docstring,
304
+ rendering_style: RenderingStyle = RenderingStyle.COMPACT,
305
+ indent: str = ' ',
306
+ ) -> str:
307
+ """Render a parsed docstring into docstring text.
308
+
309
+ :param docstring: parsed docstring representation
310
+ :param rendering_style: the style to render docstrings
311
+ :param indent: the characters used as indentation in the docstring string
312
+ :returns: docstring text
313
+ """
314
+
315
+ def process_one(
316
+ one: T.Union[DocstringParam, DocstringReturns, DocstringRaises],
317
+ ) -> None:
318
+ head = ''
319
+
320
+ if isinstance(one, DocstringParam):
321
+ head += one.arg_name or ''
322
+ elif isinstance(one, DocstringReturns):
323
+ head += one.return_name or ''
324
+
325
+ if isinstance(one, DocstringParam) and one.is_optional:
326
+ optional = (
327
+ '?'
328
+ if rendering_style == RenderingStyle.COMPACT
329
+ else ', optional'
330
+ )
331
+ else:
332
+ optional = ''
333
+
334
+ if one.type_name and head:
335
+ head += f' ({one.type_name}{optional}):'
336
+ elif one.type_name:
337
+ head += f'{one.type_name}{optional}:'
338
+ else:
339
+ head += ':'
340
+ head = indent + head
341
+
342
+ if one.description and rendering_style == RenderingStyle.EXPANDED:
343
+ body = f'\n{indent}{indent}'.join(
344
+ [head] + one.description.splitlines(),
345
+ )
346
+ parts.append(body)
347
+ elif one.description:
348
+ (first, *rest) = one.description.splitlines()
349
+ body = f'\n{indent}{indent}'.join([head + ' ' + first] + rest)
350
+ parts.append(body)
351
+ else:
352
+ parts.append(head)
353
+
354
+ def process_sect(name: str, args: T.List[T.Any]) -> None:
355
+ if args:
356
+ parts.append(name)
357
+ for arg in args:
358
+ process_one(arg)
359
+ parts.append('')
360
+
361
+ parts: T.List[str] = []
362
+ if docstring.short_description:
363
+ parts.append(docstring.short_description)
364
+ if docstring.blank_after_short_description:
365
+ parts.append('')
366
+
367
+ if docstring.long_description:
368
+ parts.append(docstring.long_description)
369
+ if docstring.blank_after_long_description:
370
+ parts.append('')
371
+
372
+ process_sect(
373
+ 'Args:', [p for p in docstring.params or [] if p.args[0] == 'param'],
374
+ )
375
+
376
+ process_sect(
377
+ 'Attributes:',
378
+ [p for p in docstring.params or [] if p.args[0] == 'attribute'],
379
+ )
380
+
381
+ process_sect(
382
+ 'Returns:',
383
+ [p for p in docstring.many_returns or [] if not p.is_generator],
384
+ )
385
+
386
+ process_sect(
387
+ 'Yields:', [p for p in docstring.many_returns or [] if p.is_generator],
388
+ )
389
+
390
+ process_sect('Raises:', docstring.raises or [])
391
+
392
+ if docstring.returns and not docstring.many_returns:
393
+ ret = docstring.returns
394
+ parts.append('Yields:' if ret else 'Returns:')
395
+ parts.append('-' * len(parts[-1]))
396
+ process_one(ret)
397
+
398
+ for meta in docstring.meta:
399
+ if isinstance(
400
+ meta, (DocstringParam, DocstringReturns, DocstringRaises),
401
+ ):
402
+ continue # Already handled
403
+ parts.append(meta.args[0].replace('_', '').title() + ':')
404
+ if meta.description:
405
+ lines = [indent + m for m in meta.description.splitlines()]
406
+ parts.append('\n'.join(lines))
407
+ parts.append('')
408
+
409
+ while parts and not parts[-1]:
410
+ parts.pop()
411
+
412
+ return '\n'.join(parts)