Path: blob/main/singlestoredb/docstring/tests/test_epydoc.py
469 views
"""Tests for epydoc-style docstring routines."""1import typing as T23import pytest45from singlestoredb.docstring.common import ParseError6from singlestoredb.docstring.common import RenderingStyle7from singlestoredb.docstring.epydoc import compose8from singlestoredb.docstring.epydoc import parse91011@pytest.mark.parametrize(12'source, expected',13[14pytest.param(None, None, id='No __doc__'),15('', None),16('\n', None),17('Short description', 'Short description'),18('\nShort description\n', 'Short description'),19('\n Short description\n', 'Short description'),20],21)22def test_short_description(23source: T.Optional[str], expected: T.Optional[str],24) -> None:25"""Test parsing short description."""26docstring = parse(source)27assert docstring.short_description == expected28assert docstring.long_description is None29assert not docstring.meta303132@pytest.mark.parametrize(33'source, expected_short_desc, expected_long_desc, expected_blank',34[35(36'Short description\n\nLong description',37'Short description',38'Long description',39True,40),41(42"""43Short description4445Long description46""",47'Short description',48'Long description',49True,50),51(52"""53Short description5455Long description56Second line57""",58'Short description',59'Long description\nSecond line',60True,61),62(63'Short description\nLong description',64'Short description',65'Long description',66False,67),68(69"""70Short description71Long description72""",73'Short description',74'Long description',75False,76),77(78'\nShort description\nLong description\n',79'Short description',80'Long description',81False,82),83(84"""85Short description86Long description87Second line88""",89'Short description',90'Long description\nSecond line',91False,92),93],94)95def test_long_description(96source: str,97expected_short_desc: str,98expected_long_desc: str,99expected_blank: bool,100) -> None:101"""Test parsing long description."""102docstring = parse(source)103assert docstring.short_description == expected_short_desc104assert docstring.long_description == expected_long_desc105assert docstring.blank_after_short_description == expected_blank106assert not docstring.meta107108109@pytest.mark.parametrize(110'source, expected_short_desc, expected_long_desc, '111'expected_blank_short_desc, expected_blank_long_desc',112[113(114"""115Short description116@meta: asd117""",118'Short description',119None,120False,121False,122),123(124"""125Short description126Long description127@meta: asd128""",129'Short description',130'Long description',131False,132False,133),134(135"""136Short description137First line138Second line139@meta: asd140""",141'Short description',142'First line\n Second line',143False,144False,145),146(147"""148Short description149150First line151Second line152@meta: asd153""",154'Short description',155'First line\n Second line',156True,157False,158),159(160"""161Short description162163First line164Second line165166@meta: asd167""",168'Short description',169'First line\n Second line',170True,171True,172),173(174"""175@meta: asd176""",177None,178None,179False,180False,181),182],183)184def test_meta_newlines(185source: str,186expected_short_desc: T.Optional[str],187expected_long_desc: T.Optional[str],188expected_blank_short_desc: bool,189expected_blank_long_desc: bool,190) -> None:191"""Test parsing newlines around description sections."""192docstring = parse(source)193assert docstring.short_description == expected_short_desc194assert docstring.long_description == expected_long_desc195assert docstring.blank_after_short_description == expected_blank_short_desc196assert docstring.blank_after_long_description == expected_blank_long_desc197assert len(docstring.meta) == 1198199200def test_meta_with_multiline_description() -> None:201"""Test parsing multiline meta documentation."""202docstring = parse(203"""204Short description205206@meta: asd207120822093210""",211)212assert docstring.short_description == 'Short description'213assert len(docstring.meta) == 1214assert docstring.meta[0].args == ['meta']215assert docstring.meta[0].description == 'asd\n1\n 2\n3'216217218def test_multiple_meta() -> None:219"""Test parsing multiple meta."""220docstring = parse(221"""222Short description223224@meta1: asd225122622273228@meta2: herp229@meta3: derp230""",231)232assert docstring.short_description == 'Short description'233assert len(docstring.meta) == 3234assert docstring.meta[0].args == ['meta1']235assert docstring.meta[0].description == 'asd\n1\n 2\n3'236assert docstring.meta[1].args == ['meta2']237assert docstring.meta[1].description == 'herp'238assert docstring.meta[2].args == ['meta3']239assert docstring.meta[2].description == 'derp'240241242def test_meta_with_args() -> None:243"""Test parsing meta with additional arguments."""244docstring = parse(245"""246Short description247248@meta ene due rabe: asd249""",250)251assert docstring.short_description == 'Short description'252assert len(docstring.meta) == 1253assert docstring.meta[0].args == ['meta', 'ene', 'due', 'rabe']254assert docstring.meta[0].description == 'asd'255256257def test_params() -> None:258"""Test parsing params."""259docstring = parse('Short description')260assert len(docstring.params) == 0261262docstring = parse(263"""264Short description265266@param name: description 1267@param priority: description 2268@type priority: int269@param sender: description 3270@type sender: str?271@param message: description 4, defaults to 'hello'272@type message: str?273@param multiline: long description 5,274defaults to 'bye'275@type multiline: str?276""",277)278assert len(docstring.params) == 5279assert docstring.params[0].arg_name == 'name'280assert docstring.params[0].type_name is None281assert docstring.params[0].description == 'description 1'282assert docstring.params[0].default is None283assert not docstring.params[0].is_optional284assert docstring.params[1].arg_name == 'priority'285assert docstring.params[1].type_name == 'int'286assert docstring.params[1].description == 'description 2'287assert not docstring.params[1].is_optional288assert docstring.params[1].default is None289assert docstring.params[2].arg_name == 'sender'290assert docstring.params[2].type_name == 'str'291assert docstring.params[2].description == 'description 3'292assert docstring.params[2].is_optional293assert docstring.params[2].default is None294assert docstring.params[3].arg_name == 'message'295assert docstring.params[3].type_name == 'str'296assert (297docstring.params[3].description == "description 4, defaults to 'hello'"298)299assert docstring.params[3].is_optional300assert docstring.params[3].default == "'hello'"301assert docstring.params[4].arg_name == 'multiline'302assert docstring.params[4].type_name == 'str'303assert (304docstring.params[4].description305== "long description 5,\ndefaults to 'bye'"306)307assert docstring.params[4].is_optional308assert docstring.params[4].default == "'bye'"309310311def test_returns() -> None:312"""Test parsing returns."""313docstring = parse(314"""315Short description316""",317)318assert docstring.returns is None319320docstring = parse(321"""322Short description323@return: description324""",325)326assert docstring.returns is not None327assert docstring.returns.type_name is None328assert docstring.returns.description == 'description'329assert not docstring.returns.is_generator330331docstring = parse(332"""333Short description334@return: description335@rtype: int336""",337)338assert docstring.returns is not None339assert docstring.returns.type_name == 'int'340assert docstring.returns.description == 'description'341assert not docstring.returns.is_generator342343344def test_yields() -> None:345"""Test parsing yields."""346docstring = parse(347"""348Short description349""",350)351assert docstring.returns is None352353docstring = parse(354"""355Short description356@yield: description357""",358)359assert docstring.returns is not None360assert docstring.returns.type_name is None361assert docstring.returns.description == 'description'362assert docstring.returns.is_generator363364docstring = parse(365"""366Short description367@yield: description368@ytype: int369""",370)371assert docstring.returns is not None372assert docstring.returns.type_name == 'int'373assert docstring.returns.description == 'description'374assert docstring.returns.is_generator375376377def test_raises() -> None:378"""Test parsing raises."""379docstring = parse(380"""381Short description382""",383)384assert len(docstring.raises) == 0385386docstring = parse(387"""388Short description389@raise: description390""",391)392assert len(docstring.raises) == 1393assert docstring.raises[0].type_name is None394assert docstring.raises[0].description == 'description'395396docstring = parse(397"""398Short description399@raise ValueError: description400""",401)402assert len(docstring.raises) == 1403assert docstring.raises[0].type_name == 'ValueError'404assert docstring.raises[0].description == 'description'405406407def test_broken_meta() -> None:408"""Test parsing broken meta."""409with pytest.raises(ParseError):410parse('@')411412with pytest.raises(ParseError):413parse('@param herp derp')414415with pytest.raises(ParseError):416parse('@param: invalid')417418with pytest.raises(ParseError):419parse('@param with too many args: desc')420421# these should not raise any errors422parse('@sthstrange: desc')423424425@pytest.mark.parametrize(426'source, expected',427[428('', ''),429('\n', ''),430('Short description', 'Short description'),431('\nShort description\n', 'Short description'),432('\n Short description\n', 'Short description'),433(434'Short description\n\nLong description',435'Short description\n\nLong description',436),437(438"""439Short description440441Long description442""",443'Short description\n\nLong description',444),445(446"""447Short description448449Long description450Second line451""",452'Short description\n\nLong description\nSecond line',453),454(455'Short description\nLong description',456'Short description\nLong description',457),458(459"""460Short description461Long description462""",463'Short description\nLong description',464),465(466'\nShort description\nLong description\n',467'Short description\nLong description',468),469(470"""471Short description472Long description473Second line474""",475'Short description\nLong description\nSecond line',476),477(478"""479Short description480@meta: asd481""",482'Short description\n@meta: asd',483),484(485"""486Short description487Long description488@meta: asd489""",490'Short description\nLong description\n@meta: asd',491),492(493"""494Short description495First line496Second line497@meta: asd498""",499'Short description\nFirst line\n Second line\n@meta: asd',500),501(502"""503Short description504505First line506Second line507@meta: asd508""",509'Short description\n'510'\n'511'First line\n'512' Second line\n'513'@meta: asd',514),515(516"""517Short description518519First line520Second line521522@meta: asd523""",524'Short description\n'525'\n'526'First line\n'527' Second line\n'528'\n'529'@meta: asd',530),531(532"""533@meta: asd534""",535'@meta: asd',536),537(538"""539Short description540541@meta: asd542154325443545""",546'Short description\n'547'\n'548'@meta: asd\n'549' 1\n'550' 2\n'551' 3',552),553(554"""555Short description556557@meta1: asd558155925603561@meta2: herp562@meta3: derp563""",564'Short description\n'565'\n@meta1: asd\n'566' 1\n'567' 2\n'568' 3\n@meta2: herp\n'569'@meta3: derp',570),571(572"""573Short description574575@meta ene due rabe: asd576""",577'Short description\n\n@meta ene due rabe: asd',578),579(580"""581Short description582583@param name: description 1584@param priority: description 2585@type priority: int586@param sender: description 3587@type sender: str?588@type message: str?589@param message: description 4, defaults to 'hello'590@type multiline: str?591@param multiline: long description 5,592defaults to 'bye'593""",594'Short description\n'595'\n'596'@param name: description 1\n'597'@type priority: int\n'598'@param priority: description 2\n'599'@type sender: str?\n'600'@param sender: description 3\n'601'@type message: str?\n'602"@param message: description 4, defaults to 'hello'\n"603'@type multiline: str?\n'604'@param multiline: long description 5,\n'605" defaults to 'bye'",606),607(608"""609Short description610@raise: description611""",612'Short description\n@raise: description',613),614(615"""616Short description617@raise ValueError: description618""",619'Short description\n@raise ValueError: description',620),621],622)623def test_compose(source: str, expected: str) -> None:624"""Test compose in default mode."""625assert compose(parse(source)) == expected626627628@pytest.mark.parametrize(629'source, expected',630[631(632"""633Short description634635@param name: description 1636@param priority: description 2637@type priority: int638@param sender: description 3639@type sender: str?640@type message: str?641@param message: description 4, defaults to 'hello'642@type multiline: str?643@param multiline: long description 5,644defaults to 'bye'645""",646'Short description\n'647'\n'648'@param name:\n'649' description 1\n'650'@type priority: int\n'651'@param priority:\n'652' description 2\n'653'@type sender: str?\n'654'@param sender:\n'655' description 3\n'656'@type message: str?\n'657'@param message:\n'658" description 4, defaults to 'hello'\n"659'@type multiline: str?\n'660'@param multiline:\n'661' long description 5,\n'662" defaults to 'bye'",663),664],665)666def test_compose_clean(source: str, expected: str) -> None:667"""Test compose in clean mode."""668assert (669compose(parse(source), rendering_style=RenderingStyle.CLEAN)670== expected671)672673674@pytest.mark.parametrize(675'source, expected',676[677(678"""679Short description680681@param name: description 1682@param priority: description 2683@type priority: int684@param sender: description 3685@type sender: str?686@type message: str?687@param message: description 4, defaults to 'hello'688@type multiline: str?689@param multiline: long description 5,690defaults to 'bye'691""",692'Short description\n'693'\n'694'@param name:\n'695' description 1\n'696'@type priority:\n'697' int\n'698'@param priority:\n'699' description 2\n'700'@type sender:\n'701' str?\n'702'@param sender:\n'703' description 3\n'704'@type message:\n'705' str?\n'706'@param message:\n'707" description 4, defaults to 'hello'\n"708'@type multiline:\n'709' str?\n'710'@param multiline:\n'711' long description 5,\n'712" defaults to 'bye'",713),714],715)716def test_compose_expanded(source: str, expected: str) -> None:717"""Test compose in expanded mode."""718assert (719compose(parse(source), rendering_style=RenderingStyle.EXPANDED)720== expected721)722723724def test_short_rtype() -> None:725"""Test abbreviated docstring with only return type information."""726string = 'Short description.\n\n@rtype: float'727docstring = parse(string)728assert compose(docstring) == string729730731