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,1007 @@
1
+ """Tests for Google-style docstring routines."""
2
+ import typing as T
3
+
4
+ import pytest
5
+
6
+ import singlestoredb.docstring.google as google
7
+ from singlestoredb.docstring.common import ParseError
8
+ from singlestoredb.docstring.common import RenderingStyle
9
+ from singlestoredb.docstring.google import compose
10
+ from singlestoredb.docstring.google import GoogleParser
11
+ from singlestoredb.docstring.google import parse
12
+ from singlestoredb.docstring.google import Section
13
+ from singlestoredb.docstring.google import SectionType
14
+
15
+
16
+ def test_google_parser_unknown_section() -> None:
17
+ """Test parsing an unknown section with default GoogleParser
18
+ configuration.
19
+ """
20
+ parser = GoogleParser()
21
+ docstring = parser.parse(
22
+ """
23
+ Unknown:
24
+ spam: a
25
+ """,
26
+ )
27
+ assert docstring.short_description == 'Unknown:'
28
+ assert docstring.long_description == 'spam: a'
29
+ assert len(docstring.meta) == 0
30
+
31
+
32
+ def test_google_parser_multi_line_parameter_type() -> None:
33
+ """Test parsing a multi-line parameter type with default GoogleParser"""
34
+ parser = GoogleParser()
35
+ docstring = parser.parse(
36
+ """Description of the function.
37
+
38
+ Args:
39
+ output_type (Literal["searchResults", "sourcedAnswer",
40
+ "structured"]): The type of output.
41
+ This can be one of the following:
42
+ - "searchResults": Represents the search results.
43
+ - "sourcedAnswer": Represents a sourced answer.
44
+ - "structured": Represents a structured output format.
45
+
46
+ Returns:
47
+ bool: Indicates success or failure.
48
+
49
+ """,
50
+ )
51
+ assert docstring.params[0].arg_name == 'output_type'
52
+
53
+
54
+ def test_google_parser_custom_sections() -> None:
55
+ """Test parsing an unknown section with custom GoogleParser
56
+ configuration.
57
+ """
58
+ parser = GoogleParser(
59
+ [
60
+ Section('DESCRIPTION', 'desc', SectionType.SINGULAR),
61
+ Section('ARGUMENTS', 'param', SectionType.MULTIPLE),
62
+ Section('ATTRIBUTES', 'attribute', SectionType.MULTIPLE),
63
+ Section('EXAMPLES', 'examples', SectionType.SINGULAR),
64
+ ],
65
+ title_colon=False,
66
+ )
67
+ docstring = parser.parse(
68
+ """
69
+ DESCRIPTION
70
+ This is the description.
71
+
72
+ ARGUMENTS
73
+ arg1: first arg
74
+ arg2: second arg
75
+
76
+ ATTRIBUTES
77
+ attr1: first attribute
78
+ attr2: second attribute
79
+
80
+ EXAMPLES
81
+ Many examples
82
+ More examples
83
+ """,
84
+ )
85
+
86
+ assert docstring.short_description is None
87
+ assert docstring.long_description is None
88
+ assert len(docstring.meta) == 6
89
+ assert docstring.meta[0].args == ['desc']
90
+ assert docstring.meta[0].description == 'This is the description.'
91
+ assert docstring.meta[1].args == ['param', 'arg1']
92
+ assert docstring.meta[1].description == 'first arg'
93
+ assert docstring.meta[2].args == ['param', 'arg2']
94
+ assert docstring.meta[2].description == 'second arg'
95
+ assert docstring.meta[3].args == ['attribute', 'attr1']
96
+ assert docstring.meta[3].description == 'first attribute'
97
+ assert docstring.meta[4].args == ['attribute', 'attr2']
98
+ assert docstring.meta[4].description == 'second attribute'
99
+ assert docstring.meta[5].args == ['examples']
100
+ assert docstring.meta[5].description == 'Many examples\nMore examples'
101
+
102
+
103
+ def test_google_parser_custom_sections_after() -> None:
104
+ """Test parsing an unknown section with custom GoogleParser configuration
105
+ that was set at a runtime.
106
+ """
107
+ parser = GoogleParser(title_colon=False)
108
+ parser.add_section(Section('Note', 'note', SectionType.SINGULAR))
109
+ docstring = parser.parse(
110
+ """
111
+ short description
112
+
113
+ Note:
114
+ a note
115
+ """,
116
+ )
117
+ assert docstring.short_description == 'short description'
118
+ assert docstring.long_description == 'Note:\n a note'
119
+
120
+ docstring = parser.parse(
121
+ """
122
+ short description
123
+
124
+ Note a note
125
+ """,
126
+ )
127
+ assert docstring.short_description == 'short description'
128
+ assert docstring.long_description == 'Note a note'
129
+
130
+ docstring = parser.parse(
131
+ """
132
+ short description
133
+
134
+ Note
135
+ a note
136
+ """,
137
+ )
138
+ assert len(docstring.meta) == 1
139
+ assert docstring.meta[0].args == ['note']
140
+ assert docstring.meta[0].description == 'a note'
141
+
142
+
143
+ @pytest.mark.parametrize(
144
+ 'source, expected',
145
+ [
146
+ pytest.param(None, None, id='No __doc__'),
147
+ ('', None),
148
+ ('\n', None),
149
+ ('Short description', 'Short description'),
150
+ ('\nShort description\n', 'Short description'),
151
+ ('\n Short description\n', 'Short description'),
152
+ ],
153
+ )
154
+ def test_short_description(
155
+ source: T.Optional[str], expected: T.Optional[str],
156
+ ) -> None:
157
+ """Test parsing short description."""
158
+ docstring = parse(source)
159
+ assert docstring.short_description == expected
160
+ assert docstring.long_description is None
161
+ assert not docstring.meta
162
+
163
+
164
+ @pytest.mark.parametrize(
165
+ 'source, expected_short_desc, expected_long_desc, expected_blank',
166
+ [
167
+ (
168
+ 'Short description\n\nLong description',
169
+ 'Short description',
170
+ 'Long description',
171
+ True,
172
+ ),
173
+ (
174
+ """
175
+ Short description
176
+
177
+ Long description
178
+ """,
179
+ 'Short description',
180
+ 'Long description',
181
+ True,
182
+ ),
183
+ (
184
+ """
185
+ Short description
186
+
187
+ Long description
188
+ Second line
189
+ """,
190
+ 'Short description',
191
+ 'Long description\nSecond line',
192
+ True,
193
+ ),
194
+ (
195
+ 'Short description\nLong description',
196
+ 'Short description',
197
+ 'Long description',
198
+ False,
199
+ ),
200
+ (
201
+ """
202
+ Short description
203
+ Long description
204
+ """,
205
+ 'Short description',
206
+ 'Long description',
207
+ False,
208
+ ),
209
+ (
210
+ '\nShort description\nLong description\n',
211
+ 'Short description',
212
+ 'Long description',
213
+ False,
214
+ ),
215
+ (
216
+ """
217
+ Short description
218
+ Long description
219
+ Second line
220
+ """,
221
+ 'Short description',
222
+ 'Long description\nSecond line',
223
+ False,
224
+ ),
225
+ ],
226
+ )
227
+ def test_long_description(
228
+ source: str,
229
+ expected_short_desc: str,
230
+ expected_long_desc: str,
231
+ expected_blank: bool,
232
+ ) -> None:
233
+ """Test parsing long description."""
234
+ docstring = parse(source)
235
+ assert docstring.short_description == expected_short_desc
236
+ assert docstring.long_description == expected_long_desc
237
+ assert docstring.blank_after_short_description == expected_blank
238
+ assert not docstring.meta
239
+
240
+
241
+ @pytest.mark.parametrize(
242
+ 'source, expected_short_desc, expected_long_desc, '
243
+ 'expected_blank_short_desc, expected_blank_long_desc',
244
+ [
245
+ (
246
+ """
247
+ Short description
248
+ Args:
249
+ asd:
250
+ """,
251
+ 'Short description',
252
+ None,
253
+ False,
254
+ False,
255
+ ),
256
+ (
257
+ """
258
+ Short description
259
+ Long description
260
+ Args:
261
+ asd:
262
+ """,
263
+ 'Short description',
264
+ 'Long description',
265
+ False,
266
+ False,
267
+ ),
268
+ (
269
+ """
270
+ Short description
271
+ First line
272
+ Second line
273
+ Args:
274
+ asd:
275
+ """,
276
+ 'Short description',
277
+ 'First line\n Second line',
278
+ False,
279
+ False,
280
+ ),
281
+ (
282
+ """
283
+ Short description
284
+
285
+ First line
286
+ Second line
287
+ Args:
288
+ asd:
289
+ """,
290
+ 'Short description',
291
+ 'First line\n Second line',
292
+ True,
293
+ False,
294
+ ),
295
+ (
296
+ """
297
+ Short description
298
+
299
+ First line
300
+ Second line
301
+
302
+ Args:
303
+ asd:
304
+ """,
305
+ 'Short description',
306
+ 'First line\n Second line',
307
+ True,
308
+ True,
309
+ ),
310
+ (
311
+ """
312
+ Args:
313
+ asd:
314
+ """,
315
+ None,
316
+ None,
317
+ False,
318
+ False,
319
+ ),
320
+ ],
321
+ )
322
+ def test_meta_newlines(
323
+ source: str,
324
+ expected_short_desc: T.Optional[str],
325
+ expected_long_desc: T.Optional[str],
326
+ expected_blank_short_desc: bool,
327
+ expected_blank_long_desc: bool,
328
+ ) -> None:
329
+ """Test parsing newlines around description sections."""
330
+ docstring = parse(source)
331
+ assert docstring.short_description == expected_short_desc
332
+ assert docstring.long_description == expected_long_desc
333
+ assert docstring.blank_after_short_description == expected_blank_short_desc
334
+ assert docstring.blank_after_long_description == expected_blank_long_desc
335
+ assert len(docstring.meta) == 1
336
+
337
+
338
+ def test_meta_with_multiline_description() -> None:
339
+ """Test parsing multiline meta documentation."""
340
+ docstring = parse(
341
+ """
342
+ Short description
343
+
344
+ Args:
345
+ spam: asd
346
+ 1
347
+ 2
348
+ 3
349
+ """,
350
+ )
351
+ assert docstring.short_description == 'Short description'
352
+ assert len(docstring.meta) == 1
353
+ assert docstring.meta[0].args == ['param', 'spam']
354
+ assert isinstance(docstring.meta[0], google.DocstringParam)
355
+ assert docstring.meta[0].arg_name == 'spam'
356
+ assert docstring.meta[0].description == 'asd\n1\n 2\n3'
357
+
358
+
359
+ def test_default_args() -> None:
360
+ """Test parsing default arguments."""
361
+ docstring = parse(
362
+ """A sample function
363
+
364
+ A function the demonstrates docstrings
365
+
366
+ Args:
367
+ arg1 (int): The firsty arg
368
+ arg2 (str): The second arg
369
+ arg3 (float, optional): The third arg. Defaults to 1.0.
370
+ arg4 (Optional[Dict[str, Any]], optional): The last arg. Defaults to None.
371
+ arg5 (str, optional): The fifth arg. Defaults to DEFAULT_ARG5.
372
+
373
+ Returns:
374
+ Mapping[str, Any]: The args packed in a mapping
375
+ """,
376
+ )
377
+ assert docstring is not None
378
+ assert len(docstring.params) == 5
379
+
380
+ arg4 = docstring.params[3]
381
+ assert arg4.arg_name == 'arg4'
382
+ assert arg4.is_optional
383
+ assert arg4.type_name == 'Optional[Dict[str, Any]]'
384
+ assert arg4.default == 'None'
385
+ assert arg4.description == 'The last arg. Defaults to None.'
386
+
387
+
388
+ def test_multiple_meta() -> None:
389
+ """Test parsing multiple meta."""
390
+ docstring = parse(
391
+ """
392
+ Short description
393
+
394
+ Args:
395
+ spam: asd
396
+ 1
397
+ 2
398
+ 3
399
+
400
+ Raises:
401
+ bla: herp
402
+ yay: derp
403
+ """,
404
+ )
405
+ assert docstring.short_description == 'Short description'
406
+ assert len(docstring.meta) == 3
407
+ assert docstring.meta[0].args == ['param', 'spam']
408
+ assert isinstance(docstring.meta[0], google.DocstringParam)
409
+ assert docstring.meta[0].arg_name == 'spam'
410
+ assert docstring.meta[0].description == 'asd\n1\n 2\n3'
411
+ assert docstring.meta[1].args == ['raises', 'bla']
412
+ assert isinstance(docstring.meta[1], google.DocstringRaises)
413
+ assert docstring.meta[1].type_name == 'bla'
414
+ assert docstring.meta[1].description == 'herp'
415
+ assert isinstance(docstring.meta[2], google.DocstringRaises)
416
+ assert docstring.meta[2].args == ['raises', 'yay']
417
+ assert docstring.meta[2].type_name == 'yay'
418
+ assert docstring.meta[2].description == 'derp'
419
+
420
+
421
+ def test_params() -> None:
422
+ """Test parsing params."""
423
+ docstring = parse('Short description')
424
+ assert len(docstring.params) == 0
425
+
426
+ docstring = parse(
427
+ """
428
+ Short description
429
+
430
+ Args:
431
+ name: description 1
432
+ priority (int): description 2
433
+ sender (str?): description 3
434
+ ratio (Optional[float], optional): description 4
435
+ """,
436
+ )
437
+ assert len(docstring.params) == 4
438
+ assert docstring.params[0].arg_name == 'name'
439
+ assert docstring.params[0].type_name is None
440
+ assert docstring.params[0].description == 'description 1'
441
+ assert not docstring.params[0].is_optional
442
+ assert docstring.params[1].arg_name == 'priority'
443
+ assert docstring.params[1].type_name == 'int'
444
+ assert docstring.params[1].description == 'description 2'
445
+ assert not docstring.params[1].is_optional
446
+ assert docstring.params[2].arg_name == 'sender'
447
+ assert docstring.params[2].type_name == 'str'
448
+ assert docstring.params[2].description == 'description 3'
449
+ assert docstring.params[2].is_optional
450
+ assert docstring.params[3].arg_name == 'ratio'
451
+ assert docstring.params[3].type_name == 'Optional[float]'
452
+ assert docstring.params[3].description == 'description 4'
453
+ assert docstring.params[3].is_optional
454
+
455
+ docstring = parse(
456
+ """
457
+ Short description
458
+
459
+ Args:
460
+ name: description 1
461
+ with multi-line text
462
+ priority (int): description 2
463
+ """,
464
+ )
465
+ assert len(docstring.params) == 2
466
+ assert docstring.params[0].arg_name == 'name'
467
+ assert docstring.params[0].type_name is None
468
+ assert docstring.params[0].description == (
469
+ 'description 1\nwith multi-line text'
470
+ )
471
+ assert docstring.params[1].arg_name == 'priority'
472
+ assert docstring.params[1].type_name == 'int'
473
+ assert docstring.params[1].description == 'description 2'
474
+
475
+
476
+ def test_attributes() -> None:
477
+ """Test parsing attributes."""
478
+ docstring = parse('Short description')
479
+ assert len(docstring.params) == 0
480
+
481
+ docstring = parse(
482
+ """
483
+ Short description
484
+
485
+ Attributes:
486
+ name: description 1
487
+ priority (int): description 2
488
+ sender (str?): description 3
489
+ ratio (Optional[float], optional): description 4
490
+ """,
491
+ )
492
+ assert len(docstring.params) == 4
493
+ assert docstring.params[0].arg_name == 'name'
494
+ assert docstring.params[0].type_name is None
495
+ assert docstring.params[0].description == 'description 1'
496
+ assert not docstring.params[0].is_optional
497
+ assert docstring.params[1].arg_name == 'priority'
498
+ assert docstring.params[1].type_name == 'int'
499
+ assert docstring.params[1].description == 'description 2'
500
+ assert not docstring.params[1].is_optional
501
+ assert docstring.params[2].arg_name == 'sender'
502
+ assert docstring.params[2].type_name == 'str'
503
+ assert docstring.params[2].description == 'description 3'
504
+ assert docstring.params[2].is_optional
505
+ assert docstring.params[3].arg_name == 'ratio'
506
+ assert docstring.params[3].type_name == 'Optional[float]'
507
+ assert docstring.params[3].description == 'description 4'
508
+ assert docstring.params[3].is_optional
509
+
510
+ docstring = parse(
511
+ """
512
+ Short description
513
+
514
+ Attributes:
515
+ name: description 1
516
+ with multi-line text
517
+ priority (int): description 2
518
+ """,
519
+ )
520
+ assert len(docstring.params) == 2
521
+ assert docstring.params[0].arg_name == 'name'
522
+ assert docstring.params[0].type_name is None
523
+ assert docstring.params[0].description == (
524
+ 'description 1\nwith multi-line text'
525
+ )
526
+ assert docstring.params[1].arg_name == 'priority'
527
+ assert docstring.params[1].type_name == 'int'
528
+ assert docstring.params[1].description == 'description 2'
529
+
530
+
531
+ def test_returns() -> None:
532
+ """Test parsing returns."""
533
+ docstring = parse(
534
+ """
535
+ Short description
536
+ """,
537
+ )
538
+ assert docstring.returns is None
539
+ assert docstring.many_returns is not None
540
+ assert len(docstring.many_returns) == 0
541
+
542
+ docstring = parse(
543
+ """
544
+ Short description
545
+ Returns:
546
+ description
547
+ """,
548
+ )
549
+ assert docstring.returns is not None
550
+ assert docstring.returns.type_name is None
551
+ assert docstring.returns.description == 'description'
552
+ assert docstring.many_returns is not None
553
+ assert len(docstring.many_returns) == 1
554
+ assert docstring.many_returns[0] == docstring.returns
555
+
556
+ docstring = parse(
557
+ """
558
+ Short description
559
+ Returns:
560
+ description with: a colon!
561
+ """,
562
+ )
563
+ assert docstring.returns is not None
564
+ assert docstring.returns.type_name is None
565
+ assert docstring.returns.description == 'description with: a colon!'
566
+ assert docstring.many_returns is not None
567
+ assert len(docstring.many_returns) == 1
568
+ assert docstring.many_returns[0] == docstring.returns
569
+
570
+ docstring = parse(
571
+ """
572
+ Short description
573
+ Returns:
574
+ int: description
575
+ """,
576
+ )
577
+ assert docstring.returns is not None
578
+ assert docstring.returns.type_name == 'int'
579
+ assert docstring.returns.description == 'description'
580
+ assert docstring.many_returns is not None
581
+ assert len(docstring.many_returns) == 1
582
+ assert docstring.many_returns[0] == docstring.returns
583
+
584
+ docstring = parse(
585
+ """
586
+ Returns:
587
+ Optional[Mapping[str, List[int]]]: A description: with a colon
588
+ """,
589
+ )
590
+ assert docstring.returns is not None
591
+ assert docstring.returns.type_name == 'Optional[Mapping[str, List[int]]]'
592
+ assert docstring.returns.description == 'A description: with a colon'
593
+ assert docstring.many_returns is not None
594
+ assert len(docstring.many_returns) == 1
595
+ assert docstring.many_returns[0] == docstring.returns
596
+
597
+ docstring = parse(
598
+ """
599
+ Short description
600
+ Yields:
601
+ int: description
602
+ """,
603
+ )
604
+ assert docstring.returns is not None
605
+ assert docstring.returns.type_name == 'int'
606
+ assert docstring.returns.description == 'description'
607
+ assert docstring.many_returns is not None
608
+ assert len(docstring.many_returns) == 1
609
+ assert docstring.many_returns[0] == docstring.returns
610
+
611
+ docstring = parse(
612
+ """
613
+ Short description
614
+ Returns:
615
+ int: description
616
+ with much text
617
+
618
+ even some spacing
619
+ """,
620
+ )
621
+ assert docstring.returns is not None
622
+ assert docstring.returns.type_name == 'int'
623
+ assert docstring.returns.description == (
624
+ 'description\nwith much text\n\neven some spacing'
625
+ )
626
+ assert docstring.many_returns is not None
627
+ assert len(docstring.many_returns) == 1
628
+ assert docstring.many_returns[0] == docstring.returns
629
+
630
+
631
+ def test_raises() -> None:
632
+ """Test parsing raises."""
633
+ docstring = parse(
634
+ """
635
+ Short description
636
+ """,
637
+ )
638
+ assert len(docstring.raises) == 0
639
+
640
+ docstring = parse(
641
+ """
642
+ Short description
643
+ Raises:
644
+ ValueError: description
645
+ """,
646
+ )
647
+ assert len(docstring.raises) == 1
648
+ assert docstring.raises[0].type_name == 'ValueError'
649
+ assert docstring.raises[0].description == 'description'
650
+
651
+
652
+ def test_examples() -> None:
653
+ """Test parsing examples."""
654
+ docstring = parse(
655
+ """
656
+ Short description
657
+ Example:
658
+ example: 1
659
+ Examples:
660
+ long example
661
+
662
+ more here
663
+ """,
664
+ )
665
+ assert len(docstring.examples) == 2
666
+ assert docstring.examples[0].description == 'example: 1'
667
+ assert docstring.examples[1].description == 'long example\n\nmore here'
668
+
669
+
670
+ def test_broken_meta() -> None:
671
+ """Test parsing broken meta."""
672
+ with pytest.raises(ParseError):
673
+ parse('Args:')
674
+
675
+ with pytest.raises(ParseError):
676
+ parse('Args:\n herp derp')
677
+
678
+
679
+ def test_unknown_meta() -> None:
680
+ """Test parsing unknown meta."""
681
+ docstring = parse(
682
+ """Short desc
683
+
684
+ Unknown 0:
685
+ title0: content0
686
+
687
+ Args:
688
+ arg0: desc0
689
+ arg1: desc1
690
+
691
+ Unknown1:
692
+ title1: content1
693
+
694
+ Unknown2:
695
+ title2: content2
696
+ """,
697
+ )
698
+
699
+ assert docstring.params[0].arg_name == 'arg0'
700
+ assert docstring.params[0].description == 'desc0'
701
+ assert docstring.params[1].arg_name == 'arg1'
702
+ assert docstring.params[1].description == 'desc1'
703
+
704
+
705
+ def test_broken_arguments() -> None:
706
+ """Test parsing broken arguments."""
707
+ with pytest.raises(ParseError):
708
+ parse(
709
+ """This is a test
710
+
711
+ Args:
712
+ param - poorly formatted
713
+ """,
714
+ )
715
+
716
+
717
+ def test_empty_example() -> None:
718
+ """Test parsing empty examples section."""
719
+ docstring = parse(
720
+ """Short description
721
+
722
+ Example:
723
+
724
+ Raises:
725
+ IOError: some error
726
+ """,
727
+ )
728
+
729
+ assert len(docstring.examples) == 1
730
+ assert docstring.examples[0].args == ['examples']
731
+ assert docstring.examples[0].description == ''
732
+
733
+
734
+ @pytest.mark.parametrize(
735
+ 'source, expected',
736
+ [
737
+ ('', ''),
738
+ ('\n', ''),
739
+ ('Short description', 'Short description'),
740
+ ('\nShort description\n', 'Short description'),
741
+ ('\n Short description\n', 'Short description'),
742
+ (
743
+ 'Short description\n\nLong description',
744
+ 'Short description\n\nLong description',
745
+ ),
746
+ (
747
+ """
748
+ Short description
749
+
750
+ Long description
751
+ """,
752
+ 'Short description\n\nLong description',
753
+ ),
754
+ (
755
+ """
756
+ Short description
757
+
758
+ Long description
759
+ Second line
760
+ """,
761
+ 'Short description\n\nLong description\nSecond line',
762
+ ),
763
+ (
764
+ 'Short description\nLong description',
765
+ 'Short description\nLong description',
766
+ ),
767
+ (
768
+ """
769
+ Short description
770
+ Long description
771
+ """,
772
+ 'Short description\nLong description',
773
+ ),
774
+ (
775
+ '\nShort description\nLong description\n',
776
+ 'Short description\nLong description',
777
+ ),
778
+ (
779
+ """
780
+ Short description
781
+ Long description
782
+ Second line
783
+ """,
784
+ 'Short description\nLong description\nSecond line',
785
+ ),
786
+ (
787
+ """
788
+ Short description
789
+ Meta:
790
+ asd
791
+ """,
792
+ 'Short description\nMeta:\n asd',
793
+ ),
794
+ (
795
+ """
796
+ Short description
797
+ Long description
798
+ Meta:
799
+ asd
800
+ """,
801
+ 'Short description\nLong description\nMeta:\n asd',
802
+ ),
803
+ (
804
+ """
805
+ Short description
806
+ First line
807
+ Second line
808
+ Meta:
809
+ asd
810
+ """,
811
+ 'Short description\n'
812
+ 'First line\n'
813
+ ' Second line\n'
814
+ 'Meta:\n'
815
+ ' asd',
816
+ ),
817
+ (
818
+ """
819
+ Short description
820
+
821
+ First line
822
+ Second line
823
+ Meta:
824
+ asd
825
+ """,
826
+ 'Short description\n'
827
+ '\n'
828
+ 'First line\n'
829
+ ' Second line\n'
830
+ 'Meta:\n'
831
+ ' asd',
832
+ ),
833
+ (
834
+ """
835
+ Short description
836
+
837
+ First line
838
+ Second line
839
+
840
+ Meta:
841
+ asd
842
+ """,
843
+ 'Short description\n'
844
+ '\n'
845
+ 'First line\n'
846
+ ' Second line\n'
847
+ '\n'
848
+ 'Meta:\n'
849
+ ' asd',
850
+ ),
851
+ (
852
+ """
853
+ Short description
854
+
855
+ Meta:
856
+ asd
857
+ 1
858
+ 2
859
+ 3
860
+ """,
861
+ 'Short description\n'
862
+ '\n'
863
+ 'Meta:\n'
864
+ ' asd\n'
865
+ ' 1\n'
866
+ ' 2\n'
867
+ ' 3',
868
+ ),
869
+ (
870
+ """
871
+ Short description
872
+
873
+ Meta1:
874
+ asd
875
+ 1
876
+ 2
877
+ 3
878
+ Meta2:
879
+ herp
880
+ Meta3:
881
+ derp
882
+ """,
883
+ 'Short description\n'
884
+ '\n'
885
+ 'Meta1:\n'
886
+ ' asd\n'
887
+ ' 1\n'
888
+ ' 2\n'
889
+ ' 3\n'
890
+ 'Meta2:\n'
891
+ ' herp\n'
892
+ 'Meta3:\n'
893
+ ' derp',
894
+ ),
895
+ (
896
+ """
897
+ Short description
898
+
899
+ Args:
900
+ name: description 1
901
+ priority (int): description 2
902
+ sender (str, optional): description 3
903
+ message (str, optional): description 4, defaults to 'hello'
904
+ multiline (str?):
905
+ long description 5,
906
+ defaults to 'bye'
907
+ """,
908
+ 'Short description\n'
909
+ '\n'
910
+ 'Args:\n'
911
+ ' name: description 1\n'
912
+ ' priority (int): description 2\n'
913
+ ' sender (str?): description 3\n'
914
+ " message (str?): description 4, defaults to 'hello'\n"
915
+ ' multiline (str?): long description 5,\n'
916
+ " defaults to 'bye'",
917
+ ),
918
+ (
919
+ """
920
+ Short description
921
+ Raises:
922
+ ValueError: description
923
+ """,
924
+ 'Short description\nRaises:\n ValueError: description',
925
+ ),
926
+ ],
927
+ )
928
+ def test_compose(source: str, expected: str) -> None:
929
+ """Test compose in default mode."""
930
+ assert compose(parse(source)) == expected
931
+
932
+
933
+ @pytest.mark.parametrize(
934
+ 'source, expected',
935
+ [
936
+ (
937
+ """
938
+ Short description
939
+
940
+ Args:
941
+ name: description 1
942
+ priority (int): description 2
943
+ sender (str, optional): description 3
944
+ message (str, optional): description 4, defaults to 'hello'
945
+ multiline (str?):
946
+ long description 5,
947
+ defaults to 'bye'
948
+ """,
949
+ 'Short description\n'
950
+ '\n'
951
+ 'Args:\n'
952
+ ' name: description 1\n'
953
+ ' priority (int): description 2\n'
954
+ ' sender (str, optional): description 3\n'
955
+ " message (str, optional): description 4, defaults to 'hello'\n"
956
+ ' multiline (str, optional): long description 5,\n'
957
+ " defaults to 'bye'",
958
+ ),
959
+ ],
960
+ )
961
+ def test_compose_clean(source: str, expected: str) -> None:
962
+ """Test compose in clean mode."""
963
+ assert (
964
+ compose(parse(source), rendering_style=RenderingStyle.CLEAN)
965
+ == expected
966
+ )
967
+
968
+
969
+ @pytest.mark.parametrize(
970
+ 'source, expected',
971
+ [
972
+ (
973
+ """
974
+ Short description
975
+
976
+ Args:
977
+ name: description 1
978
+ priority (int): description 2
979
+ sender (str, optional): description 3
980
+ message (str, optional): description 4, defaults to 'hello'
981
+ multiline (str?):
982
+ long description 5,
983
+ defaults to 'bye'
984
+ """,
985
+ 'Short description\n'
986
+ '\n'
987
+ 'Args:\n'
988
+ ' name:\n'
989
+ ' description 1\n'
990
+ ' priority (int):\n'
991
+ ' description 2\n'
992
+ ' sender (str, optional):\n'
993
+ ' description 3\n'
994
+ ' message (str, optional):\n'
995
+ " description 4, defaults to 'hello'\n"
996
+ ' multiline (str, optional):\n'
997
+ ' long description 5,\n'
998
+ " defaults to 'bye'",
999
+ ),
1000
+ ],
1001
+ )
1002
+ def test_compose_expanded(source: str, expected: str) -> None:
1003
+ """Test compose in expanded mode."""
1004
+ assert (
1005
+ compose(parse(source), rendering_style=RenderingStyle.EXPANDED)
1006
+ == expected
1007
+ )