Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
singlestore-labs
GitHub Repository: singlestore-labs/singlestoredb-python
Path: blob/main/singlestoredb/docstring/tests/test_rest.py
469 views
1
"""Tests for ReST-style docstring routines."""
2
import typing as T
3
4
import pytest
5
6
from singlestoredb.docstring.common import ParseError
7
from singlestoredb.docstring.common import RenderingStyle
8
from singlestoredb.docstring.rest import compose
9
from singlestoredb.docstring.rest import parse
10
11
12
@pytest.mark.parametrize(
13
'source, expected',
14
[
15
pytest.param(None, None, id='No __doc__'),
16
('', None),
17
('\n', None),
18
('Short description', 'Short description'),
19
('\nShort description\n', 'Short description'),
20
('\n Short description\n', 'Short description'),
21
],
22
)
23
def test_short_description(
24
source: T.Optional[str], expected: T.Optional[str],
25
) -> None:
26
"""Test parsing short description."""
27
docstring = parse(source)
28
assert docstring.short_description == expected
29
assert docstring.description == expected
30
assert docstring.long_description is None
31
assert not docstring.meta
32
33
34
@pytest.mark.parametrize(
35
'source, expected_short_desc, expected_long_desc, expected_blank',
36
[
37
(
38
'Short description\n\nLong description',
39
'Short description',
40
'Long description',
41
True,
42
),
43
(
44
"""
45
Short description
46
47
Long description
48
""",
49
'Short description',
50
'Long description',
51
True,
52
),
53
(
54
"""
55
Short description
56
57
Long description
58
Second line
59
""",
60
'Short description',
61
'Long description\nSecond line',
62
True,
63
),
64
(
65
'Short description\nLong description',
66
'Short description',
67
'Long description',
68
False,
69
),
70
(
71
"""
72
Short description
73
Long description
74
""",
75
'Short description',
76
'Long description',
77
False,
78
),
79
(
80
'\nShort description\nLong description\n',
81
'Short description',
82
'Long description',
83
False,
84
),
85
(
86
"""
87
Short description
88
Long description
89
Second line
90
""",
91
'Short description',
92
'Long description\nSecond line',
93
False,
94
),
95
],
96
)
97
def test_long_description(
98
source: str,
99
expected_short_desc: str,
100
expected_long_desc: str,
101
expected_blank: bool,
102
) -> None:
103
"""Test parsing long description."""
104
docstring = parse(source)
105
assert docstring.short_description == expected_short_desc
106
assert docstring.long_description == expected_long_desc
107
assert docstring.blank_after_short_description == expected_blank
108
assert not docstring.meta
109
110
111
@pytest.mark.parametrize(
112
'source, expected_short_desc, expected_long_desc, '
113
'expected_blank_short_desc, expected_blank_long_desc, '
114
'expected_full_desc',
115
[
116
(
117
"""
118
Short description
119
:meta: asd
120
""",
121
'Short description',
122
None,
123
False,
124
False,
125
'Short description',
126
),
127
(
128
"""
129
Short description
130
Long description
131
:meta: asd
132
""",
133
'Short description',
134
'Long description',
135
False,
136
False,
137
'Short description\nLong description',
138
),
139
(
140
"""
141
Short description
142
First line
143
Second line
144
:meta: asd
145
""",
146
'Short description',
147
'First line\n Second line',
148
False,
149
False,
150
'Short description\nFirst line\n Second line',
151
),
152
(
153
"""
154
Short description
155
156
First line
157
Second line
158
:meta: asd
159
""",
160
'Short description',
161
'First line\n Second line',
162
True,
163
False,
164
'Short description\n\nFirst line\n Second line',
165
),
166
(
167
"""
168
Short description
169
170
First line
171
Second line
172
173
:meta: asd
174
""",
175
'Short description',
176
'First line\n Second line',
177
True,
178
True,
179
'Short description\n\nFirst line\n Second line',
180
),
181
(
182
"""
183
:meta: asd
184
""",
185
None,
186
None,
187
False,
188
False,
189
None,
190
),
191
],
192
)
193
def test_meta_newlines(
194
source: str,
195
expected_short_desc: T.Optional[str],
196
expected_long_desc: T.Optional[str],
197
expected_blank_short_desc: bool,
198
expected_blank_long_desc: bool,
199
expected_full_desc: T.Optional[str],
200
) -> None:
201
"""Test parsing newlines around description sections."""
202
docstring = parse(source)
203
assert docstring.short_description == expected_short_desc
204
assert docstring.long_description == expected_long_desc
205
assert docstring.blank_after_short_description == expected_blank_short_desc
206
assert docstring.blank_after_long_description == expected_blank_long_desc
207
assert docstring.description == expected_full_desc
208
assert len(docstring.meta) == 1
209
210
211
def test_meta_with_multiline_description() -> None:
212
"""Test parsing multiline meta documentation."""
213
docstring = parse(
214
"""
215
Short description
216
217
:meta: asd
218
1
219
2
220
3
221
""",
222
)
223
assert docstring.short_description == 'Short description'
224
assert len(docstring.meta) == 1
225
assert docstring.meta[0].args == ['meta']
226
assert docstring.meta[0].description == 'asd\n1\n 2\n3'
227
228
229
def test_multiple_meta() -> None:
230
"""Test parsing multiple meta."""
231
docstring = parse(
232
"""
233
Short description
234
235
:meta1: asd
236
1
237
2
238
3
239
:meta2: herp
240
:meta3: derp
241
""",
242
)
243
assert docstring.short_description == 'Short description'
244
assert len(docstring.meta) == 3
245
assert docstring.meta[0].args == ['meta1']
246
assert docstring.meta[0].description == 'asd\n1\n 2\n3'
247
assert docstring.meta[1].args == ['meta2']
248
assert docstring.meta[1].description == 'herp'
249
assert docstring.meta[2].args == ['meta3']
250
assert docstring.meta[2].description == 'derp'
251
252
253
def test_meta_with_args() -> None:
254
"""Test parsing meta with additional arguments."""
255
docstring = parse(
256
"""
257
Short description
258
259
:meta ene due rabe: asd
260
""",
261
)
262
assert docstring.short_description == 'Short description'
263
assert len(docstring.meta) == 1
264
assert docstring.meta[0].args == ['meta', 'ene', 'due', 'rabe']
265
assert docstring.meta[0].description == 'asd'
266
267
268
def test_params() -> None:
269
"""Test parsing params."""
270
docstring = parse('Short description')
271
assert len(docstring.params) == 0
272
273
docstring = parse(
274
"""
275
Short description
276
277
:param name: description 1
278
:param int priority: description 2
279
:param str? sender: description 3
280
:param str? message: description 4, defaults to 'hello'
281
:param str? multiline: long description 5,
282
defaults to 'bye'
283
""",
284
)
285
assert len(docstring.params) == 5
286
assert docstring.params[0].arg_name == 'name'
287
assert docstring.params[0].type_name is None
288
assert docstring.params[0].description == 'description 1'
289
assert docstring.params[0].default is None
290
assert not docstring.params[0].is_optional
291
assert docstring.params[1].arg_name == 'priority'
292
assert docstring.params[1].type_name == 'int'
293
assert docstring.params[1].description == 'description 2'
294
assert not docstring.params[1].is_optional
295
assert docstring.params[1].default is None
296
assert docstring.params[2].arg_name == 'sender'
297
assert docstring.params[2].type_name == 'str'
298
assert docstring.params[2].description == 'description 3'
299
assert docstring.params[2].is_optional
300
assert docstring.params[2].default is None
301
assert docstring.params[3].arg_name == 'message'
302
assert docstring.params[3].type_name == 'str'
303
assert (
304
docstring.params[3].description == "description 4, defaults to 'hello'"
305
)
306
assert docstring.params[3].is_optional
307
assert docstring.params[3].default == "'hello'"
308
assert docstring.params[4].arg_name == 'multiline'
309
assert docstring.params[4].type_name == 'str'
310
assert (
311
docstring.params[4].description
312
== "long description 5,\ndefaults to 'bye'"
313
)
314
assert docstring.params[4].is_optional
315
assert docstring.params[4].default == "'bye'"
316
317
docstring = parse(
318
"""
319
Short description
320
321
:param a: description a
322
:type a: int
323
:param int b: description b
324
""",
325
)
326
assert len(docstring.params) == 2
327
assert docstring.params[0].arg_name == 'a'
328
assert docstring.params[0].type_name == 'int'
329
assert docstring.params[0].description == 'description a'
330
assert docstring.params[0].default is None
331
assert not docstring.params[0].is_optional
332
333
334
def test_returns() -> None:
335
"""Test parsing returns."""
336
docstring = parse(
337
"""
338
Short description
339
""",
340
)
341
assert docstring.returns is None
342
assert docstring.many_returns is not None
343
assert len(docstring.many_returns) == 0
344
345
docstring = parse(
346
"""
347
Short description
348
:returns: description
349
""",
350
)
351
assert docstring.returns is not None
352
assert docstring.returns.type_name is None
353
assert docstring.returns.description == 'description'
354
assert not docstring.returns.is_generator
355
assert docstring.many_returns == [docstring.returns]
356
357
docstring = parse(
358
"""
359
Short description
360
:returns int: description
361
""",
362
)
363
assert docstring.returns is not None
364
assert docstring.returns.type_name == 'int'
365
assert docstring.returns.description == 'description'
366
assert not docstring.returns.is_generator
367
assert docstring.many_returns == [docstring.returns]
368
369
docstring = parse(
370
"""
371
Short description
372
:returns: description
373
:rtype: int
374
""",
375
)
376
assert docstring.returns is not None
377
assert docstring.returns.type_name == 'int'
378
assert docstring.returns.description == 'description'
379
assert not docstring.returns.is_generator
380
assert docstring.many_returns == [docstring.returns]
381
382
383
def test_yields() -> None:
384
"""Test parsing yields."""
385
docstring = parse(
386
"""
387
Short description
388
""",
389
)
390
assert docstring.returns is None
391
assert docstring.many_returns is not None
392
assert len(docstring.many_returns) == 0
393
394
docstring = parse(
395
"""
396
Short description
397
:yields: description
398
""",
399
)
400
assert docstring.returns is not None
401
assert docstring.returns.type_name is None
402
assert docstring.returns.description == 'description'
403
assert docstring.returns.is_generator
404
assert docstring.many_returns is not None
405
assert len(docstring.many_returns) == 1
406
assert docstring.many_returns[0] == docstring.returns
407
408
docstring = parse(
409
"""
410
Short description
411
:yields int: description
412
""",
413
)
414
assert docstring.returns is not None
415
assert docstring.returns.type_name == 'int'
416
assert docstring.returns.description == 'description'
417
assert docstring.returns.is_generator
418
assert docstring.many_returns is not None
419
assert len(docstring.many_returns) == 1
420
assert docstring.many_returns[0] == docstring.returns
421
422
423
def test_raises() -> None:
424
"""Test parsing raises."""
425
docstring = parse(
426
"""
427
Short description
428
""",
429
)
430
assert len(docstring.raises) == 0
431
432
docstring = parse(
433
"""
434
Short description
435
:raises: description
436
""",
437
)
438
assert len(docstring.raises) == 1
439
assert docstring.raises[0].type_name is None
440
assert docstring.raises[0].description == 'description'
441
442
docstring = parse(
443
"""
444
Short description
445
:raises ValueError: description
446
""",
447
)
448
assert len(docstring.raises) == 1
449
assert docstring.raises[0].type_name == 'ValueError'
450
assert docstring.raises[0].description == 'description'
451
452
453
def test_broken_meta() -> None:
454
"""Test parsing broken meta."""
455
with pytest.raises(ParseError):
456
parse(':')
457
458
with pytest.raises(ParseError):
459
parse(':param herp derp')
460
461
with pytest.raises(ParseError):
462
parse(':param: invalid')
463
464
with pytest.raises(ParseError):
465
parse(':param with too many args: desc')
466
467
# these should not raise any errors
468
parse(':sthstrange: desc')
469
470
471
def test_deprecation() -> None:
472
"""Test parsing deprecation notes."""
473
docstring = parse(':deprecation: 1.1.0 this function will be removed')
474
assert docstring.deprecation is not None
475
assert docstring.deprecation.version == '1.1.0'
476
assert docstring.deprecation.description == 'this function will be removed'
477
478
docstring = parse(':deprecation: this function will be removed')
479
assert docstring.deprecation is not None
480
assert docstring.deprecation.version is None
481
assert docstring.deprecation.description == 'this function will be removed'
482
483
484
@pytest.mark.parametrize(
485
'rendering_style, expected',
486
[
487
(
488
RenderingStyle.COMPACT,
489
'Short description.\n'
490
'\n'
491
'Long description.\n'
492
'\n'
493
':param int foo: a description\n'
494
':param int bar: another description\n'
495
':returns float: a return',
496
),
497
(
498
RenderingStyle.CLEAN,
499
'Short description.\n'
500
'\n'
501
'Long description.\n'
502
'\n'
503
':param int foo: a description\n'
504
':param int bar: another description\n'
505
':returns float: a return',
506
),
507
(
508
RenderingStyle.EXPANDED,
509
'Short description.\n'
510
'\n'
511
'Long description.\n'
512
'\n'
513
':param foo:\n'
514
' a description\n'
515
':type foo: int\n'
516
':param bar:\n'
517
' another description\n'
518
':type bar: int\n'
519
':returns:\n'
520
' a return\n'
521
':rtype: float',
522
),
523
],
524
)
525
def test_compose(rendering_style: RenderingStyle, expected: str) -> None:
526
"""Test compose"""
527
528
docstring = parse(
529
"""
530
Short description.
531
532
Long description.
533
534
:param int foo: a description
535
:param int bar: another description
536
:return float: a return
537
""",
538
)
539
assert compose(docstring, rendering_style=rendering_style) == expected
540
541
542
def test_short_rtype() -> None:
543
"""Test abbreviated docstring with only return type information."""
544
string = 'Short description.\n\n:rtype: float'
545
docstring = parse(string)
546
rendering_style = RenderingStyle.EXPANDED
547
assert compose(docstring, rendering_style=rendering_style) == string
548
549