Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
singlestore-labs
GitHub Repository: singlestore-labs/singlestoredb-python
Path: blob/main/singlestoredb/docstring/tests/test_epydoc.py
469 views
1
"""Tests for epydoc-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.epydoc import compose
9
from singlestoredb.docstring.epydoc 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.long_description is None
30
assert not docstring.meta
31
32
33
@pytest.mark.parametrize(
34
'source, expected_short_desc, expected_long_desc, expected_blank',
35
[
36
(
37
'Short description\n\nLong description',
38
'Short description',
39
'Long description',
40
True,
41
),
42
(
43
"""
44
Short description
45
46
Long description
47
""",
48
'Short description',
49
'Long description',
50
True,
51
),
52
(
53
"""
54
Short description
55
56
Long description
57
Second line
58
""",
59
'Short description',
60
'Long description\nSecond line',
61
True,
62
),
63
(
64
'Short description\nLong description',
65
'Short description',
66
'Long description',
67
False,
68
),
69
(
70
"""
71
Short description
72
Long description
73
""",
74
'Short description',
75
'Long description',
76
False,
77
),
78
(
79
'\nShort description\nLong description\n',
80
'Short description',
81
'Long description',
82
False,
83
),
84
(
85
"""
86
Short description
87
Long description
88
Second line
89
""",
90
'Short description',
91
'Long description\nSecond line',
92
False,
93
),
94
],
95
)
96
def test_long_description(
97
source: str,
98
expected_short_desc: str,
99
expected_long_desc: str,
100
expected_blank: bool,
101
) -> None:
102
"""Test parsing long description."""
103
docstring = parse(source)
104
assert docstring.short_description == expected_short_desc
105
assert docstring.long_description == expected_long_desc
106
assert docstring.blank_after_short_description == expected_blank
107
assert not docstring.meta
108
109
110
@pytest.mark.parametrize(
111
'source, expected_short_desc, expected_long_desc, '
112
'expected_blank_short_desc, expected_blank_long_desc',
113
[
114
(
115
"""
116
Short description
117
@meta: asd
118
""",
119
'Short description',
120
None,
121
False,
122
False,
123
),
124
(
125
"""
126
Short description
127
Long description
128
@meta: asd
129
""",
130
'Short description',
131
'Long description',
132
False,
133
False,
134
),
135
(
136
"""
137
Short description
138
First line
139
Second line
140
@meta: asd
141
""",
142
'Short description',
143
'First line\n Second line',
144
False,
145
False,
146
),
147
(
148
"""
149
Short description
150
151
First line
152
Second line
153
@meta: asd
154
""",
155
'Short description',
156
'First line\n Second line',
157
True,
158
False,
159
),
160
(
161
"""
162
Short description
163
164
First line
165
Second line
166
167
@meta: asd
168
""",
169
'Short description',
170
'First line\n Second line',
171
True,
172
True,
173
),
174
(
175
"""
176
@meta: asd
177
""",
178
None,
179
None,
180
False,
181
False,
182
),
183
],
184
)
185
def test_meta_newlines(
186
source: str,
187
expected_short_desc: T.Optional[str],
188
expected_long_desc: T.Optional[str],
189
expected_blank_short_desc: bool,
190
expected_blank_long_desc: bool,
191
) -> None:
192
"""Test parsing newlines around description sections."""
193
docstring = parse(source)
194
assert docstring.short_description == expected_short_desc
195
assert docstring.long_description == expected_long_desc
196
assert docstring.blank_after_short_description == expected_blank_short_desc
197
assert docstring.blank_after_long_description == expected_blank_long_desc
198
assert len(docstring.meta) == 1
199
200
201
def test_meta_with_multiline_description() -> None:
202
"""Test parsing multiline meta documentation."""
203
docstring = parse(
204
"""
205
Short description
206
207
@meta: asd
208
1
209
2
210
3
211
""",
212
)
213
assert docstring.short_description == 'Short description'
214
assert len(docstring.meta) == 1
215
assert docstring.meta[0].args == ['meta']
216
assert docstring.meta[0].description == 'asd\n1\n 2\n3'
217
218
219
def test_multiple_meta() -> None:
220
"""Test parsing multiple meta."""
221
docstring = parse(
222
"""
223
Short description
224
225
@meta1: asd
226
1
227
2
228
3
229
@meta2: herp
230
@meta3: derp
231
""",
232
)
233
assert docstring.short_description == 'Short description'
234
assert len(docstring.meta) == 3
235
assert docstring.meta[0].args == ['meta1']
236
assert docstring.meta[0].description == 'asd\n1\n 2\n3'
237
assert docstring.meta[1].args == ['meta2']
238
assert docstring.meta[1].description == 'herp'
239
assert docstring.meta[2].args == ['meta3']
240
assert docstring.meta[2].description == 'derp'
241
242
243
def test_meta_with_args() -> None:
244
"""Test parsing meta with additional arguments."""
245
docstring = parse(
246
"""
247
Short description
248
249
@meta ene due rabe: asd
250
""",
251
)
252
assert docstring.short_description == 'Short description'
253
assert len(docstring.meta) == 1
254
assert docstring.meta[0].args == ['meta', 'ene', 'due', 'rabe']
255
assert docstring.meta[0].description == 'asd'
256
257
258
def test_params() -> None:
259
"""Test parsing params."""
260
docstring = parse('Short description')
261
assert len(docstring.params) == 0
262
263
docstring = parse(
264
"""
265
Short description
266
267
@param name: description 1
268
@param priority: description 2
269
@type priority: int
270
@param sender: description 3
271
@type sender: str?
272
@param message: description 4, defaults to 'hello'
273
@type message: str?
274
@param multiline: long description 5,
275
defaults to 'bye'
276
@type multiline: str?
277
""",
278
)
279
assert len(docstring.params) == 5
280
assert docstring.params[0].arg_name == 'name'
281
assert docstring.params[0].type_name is None
282
assert docstring.params[0].description == 'description 1'
283
assert docstring.params[0].default is None
284
assert not docstring.params[0].is_optional
285
assert docstring.params[1].arg_name == 'priority'
286
assert docstring.params[1].type_name == 'int'
287
assert docstring.params[1].description == 'description 2'
288
assert not docstring.params[1].is_optional
289
assert docstring.params[1].default is None
290
assert docstring.params[2].arg_name == 'sender'
291
assert docstring.params[2].type_name == 'str'
292
assert docstring.params[2].description == 'description 3'
293
assert docstring.params[2].is_optional
294
assert docstring.params[2].default is None
295
assert docstring.params[3].arg_name == 'message'
296
assert docstring.params[3].type_name == 'str'
297
assert (
298
docstring.params[3].description == "description 4, defaults to 'hello'"
299
)
300
assert docstring.params[3].is_optional
301
assert docstring.params[3].default == "'hello'"
302
assert docstring.params[4].arg_name == 'multiline'
303
assert docstring.params[4].type_name == 'str'
304
assert (
305
docstring.params[4].description
306
== "long description 5,\ndefaults to 'bye'"
307
)
308
assert docstring.params[4].is_optional
309
assert docstring.params[4].default == "'bye'"
310
311
312
def test_returns() -> None:
313
"""Test parsing returns."""
314
docstring = parse(
315
"""
316
Short description
317
""",
318
)
319
assert docstring.returns is None
320
321
docstring = parse(
322
"""
323
Short description
324
@return: description
325
""",
326
)
327
assert docstring.returns is not None
328
assert docstring.returns.type_name is None
329
assert docstring.returns.description == 'description'
330
assert not docstring.returns.is_generator
331
332
docstring = parse(
333
"""
334
Short description
335
@return: description
336
@rtype: int
337
""",
338
)
339
assert docstring.returns is not None
340
assert docstring.returns.type_name == 'int'
341
assert docstring.returns.description == 'description'
342
assert not docstring.returns.is_generator
343
344
345
def test_yields() -> None:
346
"""Test parsing yields."""
347
docstring = parse(
348
"""
349
Short description
350
""",
351
)
352
assert docstring.returns is None
353
354
docstring = parse(
355
"""
356
Short description
357
@yield: description
358
""",
359
)
360
assert docstring.returns is not None
361
assert docstring.returns.type_name is None
362
assert docstring.returns.description == 'description'
363
assert docstring.returns.is_generator
364
365
docstring = parse(
366
"""
367
Short description
368
@yield: description
369
@ytype: int
370
""",
371
)
372
assert docstring.returns is not None
373
assert docstring.returns.type_name == 'int'
374
assert docstring.returns.description == 'description'
375
assert docstring.returns.is_generator
376
377
378
def test_raises() -> None:
379
"""Test parsing raises."""
380
docstring = parse(
381
"""
382
Short description
383
""",
384
)
385
assert len(docstring.raises) == 0
386
387
docstring = parse(
388
"""
389
Short description
390
@raise: description
391
""",
392
)
393
assert len(docstring.raises) == 1
394
assert docstring.raises[0].type_name is None
395
assert docstring.raises[0].description == 'description'
396
397
docstring = parse(
398
"""
399
Short description
400
@raise ValueError: description
401
""",
402
)
403
assert len(docstring.raises) == 1
404
assert docstring.raises[0].type_name == 'ValueError'
405
assert docstring.raises[0].description == 'description'
406
407
408
def test_broken_meta() -> None:
409
"""Test parsing broken meta."""
410
with pytest.raises(ParseError):
411
parse('@')
412
413
with pytest.raises(ParseError):
414
parse('@param herp derp')
415
416
with pytest.raises(ParseError):
417
parse('@param: invalid')
418
419
with pytest.raises(ParseError):
420
parse('@param with too many args: desc')
421
422
# these should not raise any errors
423
parse('@sthstrange: desc')
424
425
426
@pytest.mark.parametrize(
427
'source, expected',
428
[
429
('', ''),
430
('\n', ''),
431
('Short description', 'Short description'),
432
('\nShort description\n', 'Short description'),
433
('\n Short description\n', 'Short description'),
434
(
435
'Short description\n\nLong description',
436
'Short description\n\nLong description',
437
),
438
(
439
"""
440
Short description
441
442
Long description
443
""",
444
'Short description\n\nLong description',
445
),
446
(
447
"""
448
Short description
449
450
Long description
451
Second line
452
""",
453
'Short description\n\nLong description\nSecond line',
454
),
455
(
456
'Short description\nLong description',
457
'Short description\nLong description',
458
),
459
(
460
"""
461
Short description
462
Long description
463
""",
464
'Short description\nLong description',
465
),
466
(
467
'\nShort description\nLong description\n',
468
'Short description\nLong description',
469
),
470
(
471
"""
472
Short description
473
Long description
474
Second line
475
""",
476
'Short description\nLong description\nSecond line',
477
),
478
(
479
"""
480
Short description
481
@meta: asd
482
""",
483
'Short description\n@meta: asd',
484
),
485
(
486
"""
487
Short description
488
Long description
489
@meta: asd
490
""",
491
'Short description\nLong description\n@meta: asd',
492
),
493
(
494
"""
495
Short description
496
First line
497
Second line
498
@meta: asd
499
""",
500
'Short description\nFirst line\n Second line\n@meta: asd',
501
),
502
(
503
"""
504
Short description
505
506
First line
507
Second line
508
@meta: asd
509
""",
510
'Short description\n'
511
'\n'
512
'First line\n'
513
' Second line\n'
514
'@meta: asd',
515
),
516
(
517
"""
518
Short description
519
520
First line
521
Second line
522
523
@meta: asd
524
""",
525
'Short description\n'
526
'\n'
527
'First line\n'
528
' Second line\n'
529
'\n'
530
'@meta: asd',
531
),
532
(
533
"""
534
@meta: asd
535
""",
536
'@meta: asd',
537
),
538
(
539
"""
540
Short description
541
542
@meta: asd
543
1
544
2
545
3
546
""",
547
'Short description\n'
548
'\n'
549
'@meta: asd\n'
550
' 1\n'
551
' 2\n'
552
' 3',
553
),
554
(
555
"""
556
Short description
557
558
@meta1: asd
559
1
560
2
561
3
562
@meta2: herp
563
@meta3: derp
564
""",
565
'Short description\n'
566
'\n@meta1: asd\n'
567
' 1\n'
568
' 2\n'
569
' 3\n@meta2: herp\n'
570
'@meta3: derp',
571
),
572
(
573
"""
574
Short description
575
576
@meta ene due rabe: asd
577
""",
578
'Short description\n\n@meta ene due rabe: asd',
579
),
580
(
581
"""
582
Short description
583
584
@param name: description 1
585
@param priority: description 2
586
@type priority: int
587
@param sender: description 3
588
@type sender: str?
589
@type message: str?
590
@param message: description 4, defaults to 'hello'
591
@type multiline: str?
592
@param multiline: long description 5,
593
defaults to 'bye'
594
""",
595
'Short description\n'
596
'\n'
597
'@param name: description 1\n'
598
'@type priority: int\n'
599
'@param priority: description 2\n'
600
'@type sender: str?\n'
601
'@param sender: description 3\n'
602
'@type message: str?\n'
603
"@param message: description 4, defaults to 'hello'\n"
604
'@type multiline: str?\n'
605
'@param multiline: long description 5,\n'
606
" defaults to 'bye'",
607
),
608
(
609
"""
610
Short description
611
@raise: description
612
""",
613
'Short description\n@raise: description',
614
),
615
(
616
"""
617
Short description
618
@raise ValueError: description
619
""",
620
'Short description\n@raise ValueError: description',
621
),
622
],
623
)
624
def test_compose(source: str, expected: str) -> None:
625
"""Test compose in default mode."""
626
assert compose(parse(source)) == expected
627
628
629
@pytest.mark.parametrize(
630
'source, expected',
631
[
632
(
633
"""
634
Short description
635
636
@param name: description 1
637
@param priority: description 2
638
@type priority: int
639
@param sender: description 3
640
@type sender: str?
641
@type message: str?
642
@param message: description 4, defaults to 'hello'
643
@type multiline: str?
644
@param multiline: long description 5,
645
defaults to 'bye'
646
""",
647
'Short description\n'
648
'\n'
649
'@param name:\n'
650
' description 1\n'
651
'@type priority: int\n'
652
'@param priority:\n'
653
' description 2\n'
654
'@type sender: str?\n'
655
'@param sender:\n'
656
' description 3\n'
657
'@type message: str?\n'
658
'@param message:\n'
659
" description 4, defaults to 'hello'\n"
660
'@type multiline: str?\n'
661
'@param multiline:\n'
662
' long description 5,\n'
663
" defaults to 'bye'",
664
),
665
],
666
)
667
def test_compose_clean(source: str, expected: str) -> None:
668
"""Test compose in clean mode."""
669
assert (
670
compose(parse(source), rendering_style=RenderingStyle.CLEAN)
671
== expected
672
)
673
674
675
@pytest.mark.parametrize(
676
'source, expected',
677
[
678
(
679
"""
680
Short description
681
682
@param name: description 1
683
@param priority: description 2
684
@type priority: int
685
@param sender: description 3
686
@type sender: str?
687
@type message: str?
688
@param message: description 4, defaults to 'hello'
689
@type multiline: str?
690
@param multiline: long description 5,
691
defaults to 'bye'
692
""",
693
'Short description\n'
694
'\n'
695
'@param name:\n'
696
' description 1\n'
697
'@type priority:\n'
698
' int\n'
699
'@param priority:\n'
700
' description 2\n'
701
'@type sender:\n'
702
' str?\n'
703
'@param sender:\n'
704
' description 3\n'
705
'@type message:\n'
706
' str?\n'
707
'@param message:\n'
708
" description 4, defaults to 'hello'\n"
709
'@type multiline:\n'
710
' str?\n'
711
'@param multiline:\n'
712
' long description 5,\n'
713
" defaults to 'bye'",
714
),
715
],
716
)
717
def test_compose_expanded(source: str, expected: str) -> None:
718
"""Test compose in expanded mode."""
719
assert (
720
compose(parse(source), rendering_style=RenderingStyle.EXPANDED)
721
== expected
722
)
723
724
725
def test_short_rtype() -> None:
726
"""Test abbreviated docstring with only return type information."""
727
string = 'Short description.\n\n@rtype: float'
728
docstring = parse(string)
729
assert compose(docstring) == string
730
731