Path: blob/main/singlestoredb/docstring/tests/test_numpydoc.py
469 views
"""Tests for numpydoc-style docstring routines."""1import typing as T23import pytest45import singlestoredb.docstring.numpydoc as numpydoc6from singlestoredb.docstring.numpydoc import compose7from singlestoredb.docstring.numpydoc import parse8910@pytest.mark.parametrize(11'source, expected',12[13pytest.param(None, None, id='No __doc__'),14('', None),15('\n', None),16('Short description', 'Short description'),17('\nShort description\n', 'Short description'),18('\n Short description\n', 'Short description'),19],20)21def test_short_description(22source: T.Optional[str], expected: T.Optional[str],23) -> None:24"""Test parsing short description."""25docstring = parse(source)26assert docstring.short_description == expected27assert docstring.long_description is None28assert not docstring.meta293031@pytest.mark.parametrize(32'source, expected_short_desc, expected_long_desc, expected_blank',33[34(35'Short description\n\nLong description',36'Short description',37'Long description',38True,39),40(41"""42Short description4344Long description45""",46'Short description',47'Long description',48True,49),50(51"""52Short description5354Long description55Second line56""",57'Short description',58'Long description\nSecond line',59True,60),61(62'Short description\nLong description',63'Short description',64'Long description',65False,66),67(68"""69Short description70Long description71""",72'Short description',73'Long description',74False,75),76(77'\nShort description\nLong description\n',78'Short description',79'Long description',80False,81),82(83"""84Short description85Long description86Second line87""",88'Short description',89'Long description\nSecond line',90False,91),92],93)94def test_long_description(95source: str,96expected_short_desc: str,97expected_long_desc: str,98expected_blank: bool,99) -> None:100"""Test parsing long description."""101docstring = parse(source)102assert docstring.short_description == expected_short_desc103assert docstring.long_description == expected_long_desc104assert docstring.blank_after_short_description == expected_blank105assert not docstring.meta106107108@pytest.mark.parametrize(109'source, expected_short_desc, expected_long_desc, '110'expected_blank_short_desc, expected_blank_long_desc',111[112(113"""114Short description115Parameters116----------117asd118""",119'Short description',120None,121False,122False,123),124(125"""126Short description127Long description128Parameters129----------130asd131""",132'Short description',133'Long description',134False,135False,136),137(138"""139Short description140First line141Second line142Parameters143----------144asd145""",146'Short description',147'First line\n Second line',148False,149False,150),151(152"""153Short description154155First line156Second line157Parameters158----------159asd160""",161'Short description',162'First line\n Second line',163True,164False,165),166(167"""168Short description169170First line171Second line172173Parameters174----------175asd176""",177'Short description',178'First line\n Second line',179True,180True,181),182(183"""184Parameters185----------186asd187""",188None,189None,190False,191False,192),193],194)195def test_meta_newlines(196source: str,197expected_short_desc: T.Optional[str],198expected_long_desc: T.Optional[str],199expected_blank_short_desc: bool,200expected_blank_long_desc: bool,201) -> None:202"""Test parsing newlines around description sections."""203docstring = parse(source)204assert docstring.short_description == expected_short_desc205assert docstring.long_description == expected_long_desc206assert docstring.blank_after_short_description == expected_blank_short_desc207assert docstring.blank_after_long_description == expected_blank_long_desc208assert len(docstring.meta) == 1209210211def test_meta_with_multiline_description() -> None:212"""Test parsing multiline meta documentation."""213docstring = parse(214"""215Short description216217Parameters218----------219spam220asd221122222233224""",225)226assert docstring.short_description == 'Short description'227assert len(docstring.meta) == 1228assert docstring.meta[0].args == ['param', 'spam']229assert isinstance(docstring.meta[0], numpydoc.DocstringParam)230assert docstring.meta[0].arg_name == 'spam'231assert docstring.meta[0].description == 'asd\n1\n 2\n3'232233234@pytest.mark.parametrize(235'source, expected_is_optional, expected_type_name, expected_default',236[237(238"""239Parameters240----------241arg1 : int242The first arg243""",244False,245'int',246None,247),248(249"""250Parameters251----------252arg2 : str253The second arg254""",255False,256'str',257None,258),259(260"""261Parameters262----------263arg3 : float, optional264The third arg. Default is 1.0.265""",266True,267'float',268'1.0',269),270(271"""272Parameters273----------274arg4 : Optional[Dict[str, Any]], optional275The fourth arg. Defaults to None276""",277True,278'Optional[Dict[str, Any]]',279'None',280),281(282"""283Parameters284----------285arg5 : str, optional286The fifth arg. Default: DEFAULT_ARGS287""",288True,289'str',290'DEFAULT_ARGS',291),292(293"""294Parameters295----------296parameter_without_default : int297The parameter_without_default is required.298""",299False,300'int',301None,302),303],304)305def test_default_args(306source: str,307expected_is_optional: bool,308expected_type_name: T.Optional[str],309expected_default: T.Optional[str],310) -> None:311"""Test parsing default arguments."""312docstring = parse(source)313assert docstring is not None314assert len(docstring.params) == 1315316arg1 = docstring.params[0]317assert arg1.is_optional == expected_is_optional318assert arg1.type_name == expected_type_name319assert arg1.default == expected_default320321322def test_multiple_meta() -> None:323"""Test parsing multiple meta."""324docstring = parse(325"""326Short description327328Parameters329----------330spam331asd332133323343335336Raises337------338bla339herp340yay341derp342""",343)344assert docstring.short_description == 'Short description'345assert len(docstring.meta) == 3346assert docstring.meta[0].args == ['param', 'spam']347assert isinstance(docstring.meta[0], numpydoc.DocstringParam)348assert docstring.meta[0].arg_name == 'spam'349assert docstring.meta[0].description == 'asd\n1\n 2\n3'350assert docstring.meta[1].args == ['raises', 'bla']351assert isinstance(docstring.meta[1], numpydoc.DocstringRaises)352assert docstring.meta[1].type_name == 'bla'353assert docstring.meta[1].description == 'herp'354assert docstring.meta[2].args == ['raises', 'yay']355assert isinstance(docstring.meta[2], numpydoc.DocstringRaises)356assert docstring.meta[2].type_name == 'yay'357assert docstring.meta[2].description == 'derp'358359360def test_params() -> None:361"""Test parsing params."""362docstring = parse('Short description')363assert len(docstring.params) == 0364365docstring = parse(366"""367Short description368369Parameters370----------371name372description 1373priority : int374description 2375sender : str, optional376description 3377ratio : Optional[float], optional378description 4379""",380)381assert len(docstring.params) == 4382assert docstring.params[0].arg_name == 'name'383assert docstring.params[0].type_name is None384assert docstring.params[0].description == 'description 1'385assert not docstring.params[0].is_optional386assert docstring.params[1].arg_name == 'priority'387assert docstring.params[1].type_name == 'int'388assert docstring.params[1].description == 'description 2'389assert not docstring.params[1].is_optional390assert docstring.params[2].arg_name == 'sender'391assert docstring.params[2].type_name == 'str'392assert docstring.params[2].description == 'description 3'393assert docstring.params[2].is_optional394assert docstring.params[3].arg_name == 'ratio'395assert docstring.params[3].type_name == 'Optional[float]'396assert docstring.params[3].description == 'description 4'397assert docstring.params[3].is_optional398399docstring = parse(400"""401Short description402403Parameters404----------405name406description 1407with multi-line text408priority : int409description 2410""",411)412assert len(docstring.params) == 2413assert docstring.params[0].arg_name == 'name'414assert docstring.params[0].type_name is None415assert docstring.params[0].description == (416'description 1\nwith multi-line text'417)418assert docstring.params[1].arg_name == 'priority'419assert docstring.params[1].type_name == 'int'420assert docstring.params[1].description == 'description 2'421422423def test_attributes() -> None:424"""Test parsing attributes."""425docstring = parse('Short description')426assert len(docstring.params) == 0427428docstring = parse(429"""430Short description431432Attributes433----------434name435description 1436priority : int437description 2438sender : str, optional439description 3440ratio : Optional[float], optional441description 4442""",443)444assert len(docstring.params) == 4445assert docstring.params[0].arg_name == 'name'446assert docstring.params[0].type_name is None447assert docstring.params[0].description == 'description 1'448assert not docstring.params[0].is_optional449assert docstring.params[1].arg_name == 'priority'450assert docstring.params[1].type_name == 'int'451assert docstring.params[1].description == 'description 2'452assert not docstring.params[1].is_optional453assert docstring.params[2].arg_name == 'sender'454assert docstring.params[2].type_name == 'str'455assert docstring.params[2].description == 'description 3'456assert docstring.params[2].is_optional457assert docstring.params[3].arg_name == 'ratio'458assert docstring.params[3].type_name == 'Optional[float]'459assert docstring.params[3].description == 'description 4'460assert docstring.params[3].is_optional461462docstring = parse(463"""464Short description465466Attributes467----------468name469description 1470with multi-line text471priority : int472description 2473""",474)475assert len(docstring.params) == 2476assert docstring.params[0].arg_name == 'name'477assert docstring.params[0].type_name is None478assert docstring.params[0].description == (479'description 1\nwith multi-line text'480)481assert docstring.params[1].arg_name == 'priority'482assert docstring.params[1].type_name == 'int'483assert docstring.params[1].description == 'description 2'484485486def test_other_params() -> None:487"""Test parsing other parameters."""488docstring = parse(489"""490Short description491Other Parameters492----------------493only_seldom_used_keywords : type, optional494Explanation495common_parameters_listed_above : type, optional496Explanation497""",498)499assert len(docstring.meta) == 2500assert docstring.meta[0].args == [501'other_param',502'only_seldom_used_keywords',503]504assert isinstance(docstring.meta[0], numpydoc.DocstringParam)505assert docstring.meta[0].arg_name == 'only_seldom_used_keywords'506assert docstring.meta[0].type_name == 'type'507assert docstring.meta[0].is_optional508assert docstring.meta[0].description == 'Explanation'509510assert docstring.meta[1].args == [511'other_param',512'common_parameters_listed_above',513]514515516def test_yields() -> None:517"""Test parsing yields."""518docstring = parse(519"""520Short description521Yields522------523int524description525""",526)527assert len(docstring.meta) == 1528assert isinstance(docstring.meta[0], numpydoc.DocstringReturns)529assert docstring.meta[0].args == ['yields']530assert docstring.meta[0].type_name == 'int'531assert docstring.meta[0].description == 'description'532assert docstring.meta[0].return_name is None533assert docstring.meta[0].is_generator534535536def test_returns() -> None:537"""Test parsing returns."""538docstring = parse(539"""540Short description541""",542)543assert docstring.returns is None544assert docstring.many_returns is not None545assert len(docstring.many_returns) == 0546547docstring = parse(548"""549Short description550Returns551-------552type553""",554)555assert docstring.returns is not None556assert docstring.returns.type_name == 'type'557assert docstring.returns.description is None558assert docstring.many_returns is not None559assert len(docstring.many_returns) == 1560assert docstring.many_returns[0] == docstring.returns561562docstring = parse(563"""564Short description565Returns566-------567int568description569""",570)571assert docstring.returns is not None572assert docstring.returns.type_name == 'int'573assert docstring.returns.description == 'description'574assert docstring.many_returns is not None575assert len(docstring.many_returns) == 1576assert docstring.many_returns[0] == docstring.returns577578docstring = parse(579"""580Returns581-------582Optional[Mapping[str, List[int]]]583A description: with a colon584""",585)586assert docstring.returns is not None587assert docstring.returns.type_name == 'Optional[Mapping[str, List[int]]]'588assert docstring.returns.description == 'A description: with a colon'589assert docstring.many_returns is not None590assert len(docstring.many_returns) == 1591assert docstring.many_returns[0] == docstring.returns592593docstring = parse(594"""595Short description596Returns597-------598int599description600with much text601602even some spacing603""",604)605assert docstring.returns is not None606assert docstring.returns.type_name == 'int'607assert docstring.returns.description == (608'description\nwith much text\n\neven some spacing'609)610assert docstring.many_returns is not None611assert len(docstring.many_returns) == 1612assert docstring.many_returns[0] == docstring.returns613614docstring = parse(615"""616Short description617Returns618-------619a : int620description for a621b : str622description for b623""",624)625assert docstring.returns is not None626assert docstring.returns.type_name == 'int'627assert docstring.returns.description == ('description for a')628assert docstring.many_returns is not None629assert len(docstring.many_returns) == 2630assert docstring.many_returns[0].type_name == 'int'631assert docstring.many_returns[0].description == 'description for a'632assert docstring.many_returns[0].return_name == 'a'633assert docstring.many_returns[1].type_name == 'str'634assert docstring.many_returns[1].description == 'description for b'635assert docstring.many_returns[1].return_name == 'b'636637638def test_raises() -> None:639"""Test parsing raises."""640docstring = parse(641"""642Short description643""",644)645assert len(docstring.raises) == 0646647docstring = parse(648"""649Short description650Raises651------652ValueError653description654""",655)656assert len(docstring.raises) == 1657assert docstring.raises[0].type_name == 'ValueError'658assert docstring.raises[0].description == 'description'659660661def test_warns() -> None:662"""Test parsing warns."""663docstring = parse(664"""665Short description666Warns667-----668UserWarning669description670""",671)672assert len(docstring.meta) == 1673assert isinstance(docstring.meta[0], numpydoc.DocstringRaises)674assert docstring.meta[0].type_name == 'UserWarning'675assert docstring.meta[0].description == 'description'676677678def test_simple_sections() -> None:679"""Test parsing simple sections."""680docstring = parse(681"""682Short description683684See Also685--------686something : some thing you can also see687actually, anything can go in this section688689Warnings690--------691Here be dragons692693Notes694-----695None of this is real696697References698----------699Cite the relevant literature, e.g. [1]_. You may also cite these700references in the notes section above.701702.. [1] O. McNoleg, "The integration of GIS, remote sensing,703expert systems and adaptive co-kriging for environmental habitat704modelling of the Highland Haggis using object-oriented, fuzzy-logic705and neural-network techniques," Computers & Geosciences, vol. 22,706pp. 585-588, 1996.707""",708)709assert len(docstring.meta) == 4710assert docstring.meta[0].args == ['see_also']711assert docstring.meta[0].description == (712'something : some thing you can also see\n'713'actually, anything can go in this section'714)715716assert docstring.meta[1].args == ['warnings']717assert docstring.meta[1].description == 'Here be dragons'718719assert docstring.meta[2].args == ['notes']720assert docstring.meta[2].description == 'None of this is real'721722assert docstring.meta[3].args == ['references']723724725@pytest.mark.parametrize(726'source, expected_results',727[728(729'Description\nExamples\n--------\nlong example\n\nmore here',730[731(None, 'long example\n\nmore here'),732],733),734(735'Description\nExamples\n--------\n>>> test',736[737('>>> test', ''),738],739),740(741'Description\nExamples\n--------\n>>> testa\n>>> testb',742[743('>>> testa\n>>> testb', ''),744],745),746(747'Description\nExamples\n--------\n>>> test1\ndesc1',748[749('>>> test1\ndesc1', ''),750],751),752(753'Description\nExamples\n--------\n'754'>>> test1a\n>>> test1b\ndesc1a\ndesc1b',755[756('>>> test1a\n>>> test1b\ndesc1a\ndesc1b', ''),757],758),759(760'Description\nExamples\n--------\n'761'>>> test1\ndesc1\n>>> test2\ndesc2',762[763('>>> test1\ndesc1', ''),764('>>> test2\ndesc2', ''),765],766),767(768'Description\nExamples\n--------\n'769'>>> test1a\n>>> test1b\ndesc1a\ndesc1b\n'770'>>> test2a\n>>> test2b\ndesc2a\ndesc2b\n',771[772('>>> test1a\n>>> test1b\ndesc1a\ndesc1b', ''),773('>>> test2a\n>>> test2b\ndesc2a\ndesc2b', ''),774],775),776(777'Description\nExamples\n--------\n'778' >>> test1a\n >>> test1b\n desc1a\n desc1b\n'779' >>> test2a\n >>> test2b\n desc2a\n desc2b\n',780[781('>>> test1a\n>>> test1b\ndesc1a\ndesc1b', ''),782('>>> test2a\n>>> test2b\ndesc2a\ndesc2b', ''),783],784),785],786)787def test_examples(788source: str, expected_results: T.List[T.Tuple[T.Optional[str], str]],789) -> None:790"""Test parsing examples."""791docstring = parse(source)792assert len(docstring.meta) == len(expected_results)793for meta, expected_result in zip(docstring.meta, expected_results):794assert meta.description == expected_result[1]795assert len(docstring.examples) == len(expected_results)796for example, expected_result in zip(docstring.examples, expected_results):797assert example.snippet == expected_result[0]798assert example.description == expected_result[1]799800801@pytest.mark.parametrize(802'source, expected_depr_version, expected_depr_desc',803[804(805'Short description\n\n.. deprecated:: 1.6.0\n This is busted!',806'1.6.0',807'This is busted!',808),809(810(811'Short description\n\n'812'.. deprecated:: 1.6.0\n'813' This description has\n'814' multiple lines!'815),816'1.6.0',817'This description has\nmultiple lines!',818),819('Short description\n\n.. deprecated:: 1.6.0', '1.6.0', None),820(821'Short description\n\n.. deprecated::\n No version!',822None,823'No version!',824),825],826)827def test_deprecation(828source: str,829expected_depr_version: T.Optional[str],830expected_depr_desc: T.Optional[str],831) -> None:832"""Test parsing deprecation notes."""833docstring = parse(source)834835assert docstring.deprecation is not None836assert docstring.deprecation.version == expected_depr_version837assert docstring.deprecation.description == expected_depr_desc838839840@pytest.mark.parametrize(841'source, expected',842[843('', ''),844('\n', ''),845('Short description', 'Short description'),846('\nShort description\n', 'Short description'),847('\n Short description\n', 'Short description'),848(849'Short description\n\nLong description',850'Short description\n\nLong description',851),852(853"""854Short description855856Long description857""",858'Short description\n\nLong description',859),860(861"""862Short description863864Long description865Second line866""",867'Short description\n\nLong description\nSecond line',868),869(870'Short description\nLong description',871'Short description\nLong description',872),873(874"""875Short description876Long description877""",878'Short description\nLong description',879),880(881'\nShort description\nLong description\n',882'Short description\nLong description',883),884(885"""886Short description887Long description888Second line889""",890'Short description\nLong description\nSecond line',891),892(893"""894Short description895Meta:896-----897asd898""",899'Short description\nMeta:\n-----\n asd',900),901(902"""903Short description904Long description905Meta:906-----907asd908""",909'Short description\n'910'Long description\n'911'Meta:\n'912'-----\n'913' asd',914),915(916"""917Short description918First line919Second line920Meta:921-----922asd923""",924'Short description\n'925'First line\n'926' Second line\n'927'Meta:\n'928'-----\n'929' asd',930),931(932"""933Short description934935First line936Second line937Meta:938-----939asd940""",941'Short description\n'942'\n'943'First line\n'944' Second line\n'945'Meta:\n'946'-----\n'947' asd',948),949(950"""951Short description952953First line954Second line955956Meta:957-----958asd959""",960'Short description\n'961'\n'962'First line\n'963' Second line\n'964'\n'965'Meta:\n'966'-----\n'967' asd',968),969(970"""971Short description972973Meta:974-----975asd976197729783979""",980'Short description\n'981'\n'982'Meta:\n'983'-----\n'984' asd\n'985' 1\n'986' 2\n'987' 3',988),989(990"""991Short description992993Meta1:994------995asd996199729983999Meta2:1000------1001herp1002Meta3:1003------1004derp1005""",1006'Short description\n'1007'\n'1008'Meta1:\n'1009'------\n'1010' asd\n'1011' 1\n'1012' 2\n'1013' 3\n'1014'Meta2:\n'1015'------\n'1016' herp\n'1017'Meta3:\n'1018'------\n'1019' derp',1020),1021(1022"""1023Short description10241025Parameters:1026-----------1027name1028description 11029priority: int1030description 21031sender: str, optional1032description 31033message: str, optional1034description 4, defaults to 'hello'1035multiline: str, optional1036long description 5,1037defaults to 'bye'1038""",1039'Short description\n'1040'\n'1041'Parameters:\n'1042'-----------\n'1043' name\n'1044' description 1\n'1045' priority: int\n'1046' description 2\n'1047' sender: str, optional\n'1048' description 3\n'1049' message: str, optional\n'1050" description 4, defaults to 'hello'\n"1051' multiline: str, optional\n'1052' long description 5,\n'1053" defaults to 'bye'",1054),1055(1056"""1057Short description1058Raises:1059-------1060ValueError1061description1062""",1063'Short description\n'1064'Raises:\n'1065'-------\n'1066' ValueError\n'1067' description',1068),1069(1070"""1071Description1072Examples:1073--------1074>>> test1a1075>>> test1b1076desc1a1077desc1b1078>>> test2a1079>>> test2b1080desc2a1081desc2b1082""",1083'Description\n'1084'Examples:\n'1085'--------\n'1086'>>> test1a\n'1087'>>> test1b\n'1088'desc1a\n'1089'desc1b\n'1090'>>> test2a\n'1091'>>> test2b\n'1092'desc2a\n'1093'desc2b',1094),1095],1096)1097def test_compose(source: str, expected: str) -> None:1098"""Test compose in default mode."""1099assert compose(parse(source)) == expected110011011102