Coverage for /home/runner/.local/share/hatch/env/virtual/importnb/KA2AwMZG/test.stdlib/lib/python3.9/site-packages/importnb/docstrings.py: 96%

1# coding: utf-8

2"""# Special handling of markdown cells as docstrings. 5 ctx 1asbct

3

4 Modify the Python `ast` to assign docstrings to functions when they are preceded by a Markdown cell.

5"""

6

7 import ast 5 ctx 1asbct

8

9"""# Modifying the `ast`

10

11 >>> assert isinstance(create_test, ast.Assign)

12 >>> assert isinstance(test_update, ast.Attribute)

13"""

14

15 create_test = ast.parse("""__test__ = globals().get('__test__', {})""", mode="single").body[0] 5 ctx 1asbct

16 test_update = ast.parse("""__test__.update""", mode="single").body[0].value 5 ctx 1asbct

17 str_nodes = (ast.Constant,) 5 ctx 1asbct

18

19"""`TestStrings` is an `ast.NodeTransformer` that captures `str_nodes` in the `TestStrings.strings` object.

20

21```ipython

22>>> assert isinstance(ast.parse(TestStrings().visit(ast.parse('"Test me"'))), ast.Module)

23

24```

25"""

26

27

28 class TestStrings(ast.NodeTransformer): 5 ctx 1asbct

29

30 strings = None 5 ctx 1asbct

31

32 def visit_Module(self, module): 5 ctx 1asbct

33 """`TestStrings.visit_Module` initializes the capture. After all the nodes are visit we append `create_test and test_update`

34 to populate the `"__test__"` attribute.

35 """

36 self.strings = [] 18 ctx 1dabcefghijklmnopqr

37 module = self.visit_body(module) 18 ctx 1dabcefghijklmnopqr

38 module.body += ( 18 ctx 1dabcefghijklmnopqr

39 [create_test]

40 + [

41 ast.copy_location(

42 ast.Expr(

43 ast.Call(

44 func=test_update,

45 args=[

46 ast.Dict(

47 keys=[ast.Constant("string-{}".format(node.lineno))],

48 values=[node],

49 )

50 ],

51 keywords=[],

52 )

53 ),

54 node,

55 )

56 for node in self.strings

57 ]

58 if self.strings

59 else []

60 )

61 return module 18 ctx 1dabcefghijklmnopqr

62

63 def visit_body(self, node): 5 ctx 1asbct

64 """`TestStrings.visit_body` visits nodes with a `"body"` attibute and extracts potential string tests."""

65

66 body = [] 18 ctx 1dabcefghijklmnopqr

67 if ( 18 ctx 1dabcefghijklmnopqr

68 node.body

69 and isinstance(node.body[0], ast.Expr)

70 and isinstance(node.body[0].value, str_nodes)

71 ):

72 body.append(node.body.pop(0)) 18 ctx 1dabcefghijklmnopqr

73 node.body = body + [ 18 ctx 1dabcefghijklmnopqr

74 (self.visit_body if hasattr(object, "body") else self.visit)(object)

75 for object in node.body

76 ]

77 return node 18 ctx 1dabcefghijklmnopqr

78

79 def visit_Expr(self, node): 5 ctx 1asbct

80 """`TestStrings.visit_Expr` append the `str_nodes` to `TestStrings.strings` to append to the `ast.Module`."""

81

82 if isinstance(node.value, str_nodes): 18 ctx 1dabcefghijklmnopqr

83 self.strings.append( 18 ctx 1dabcefghijklmnopqr

84 ast.copy_location(ast.Constant(node.value.value.replace("\n```", "\n")), node)

85 )

86 return node 18 ctx 1dabcefghijklmnopqr

87

88

89 def update_docstring(module): 5 ctx 1asbct

90 from functools import reduce 18 ctx 1dabcefghijklmnopqr

91

92 module.body = reduce(markdown_docstring, module.body, []) 18 ctx 1dabcefghijklmnopqr

93 return TestStrings().visit(module) 18 ctx 1dabcefghijklmnopqr

94

95

96 docstring_ast_types = ast.ClassDef, ast.FunctionDef 5 ctx 1asbct

97 try: 5 ctx 1asbct

98 docstring_ast_types += (ast.AsyncFunctionDef,) 5 ctx 1asbct

99 except:

100 ...

101

102

103 def markdown_docstring(nodes, node): 5 ctx 1asbct

104 if ( 18 ctx 1dabcefghijklmnopqr

105 len(nodes) > 1

106 and str_expr(nodes[-1])

107 and isinstance(node, docstring_ast_types)

108 and not str_expr(node.body[0])

109 ):

110 node.body.insert(0, nodes.pop()) 17 ctx 1dabcefghijklmnopq

111 return nodes.append(node) or nodes 18 ctx 1dabcefghijklmnopqr

112

113

114 def str_expr(node): 5 ctx 1asbct

115 return isinstance(node, ast.Expr) and isinstance(node.value, ast.Constant) 18 ctx 1dabcefghijklmnopqr