Coverage for /home/runner/.local/share/hatch/env/virtual/importnb/KA2AwMZG/test.interactive/lib/python3.9/site-packages/importnb/docstrings.py: 96%
37 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-02 04:03 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-02 04:03 +0000
1# coding: utf-8
2"""# Special handling of markdown cells as docstrings. 1atbcud
4Modify the Python `ast` to assign docstrings to functions when they are preceded by a Markdown cell.
5"""
7import ast 1atbcud
9"""# Modifying the `ast`
11 >>> assert isinstance(create_test, ast.Assign)
12 >>> assert isinstance(test_update, ast.Attribute)
13"""
15create_test = ast.parse("""__test__ = globals().get('__test__', {})""", mode="single").body[0] 1atbcud
16test_update = ast.parse("""__test__.update""", mode="single").body[0].value 1atbcud
17str_nodes = (ast.Constant,) 1atbcud
19"""`TestStrings` is an `ast.NodeTransformer` that captures `str_nodes` in the `TestStrings.strings` object.
21```ipython
22>>> assert isinstance(ast.parse(TestStrings().visit(ast.parse('"Test me"'))), ast.Module)
24```
25"""
28class TestStrings(ast.NodeTransformer): 1atbcud
30 strings = None 1atbcud
32 def visit_Module(self, module): 1atbcud
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 = [] 1eabcfdghivjklmnopqrs
37 module = self.visit_body(module) 1eabcfdghivjklmnopqrs
38 module.body += ( 1eabcfdghivjklmnopqrs
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 1eabcfdghivjklmnopqrs
63 def visit_body(self, node): 1atbcud
64 """`TestStrings.visit_body` visits nodes with a `"body"` attibute and extracts potential string tests."""
66 body = [] 1eabcfdghivjklmnopqrs
67 if ( 1eabcfdghivjklmnopqrs
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)) 1eabcfdghijklmnopqrs
73 node.body = body + [ 1eabcfdghivjklmnopqrs
74 (self.visit_body if hasattr(object, "body") else self.visit)(object)
75 for object in node.body
76 ]
77 return node 1eabcfdghivjklmnopqrs
79 def visit_Expr(self, node): 1atbcud
80 """`TestStrings.visit_Expr` append the `str_nodes` to `TestStrings.strings` to append to the `ast.Module`."""
82 if isinstance(node.value, str_nodes): 1eabcfdghijklmnopqrs
83 self.strings.append( 1eabcfdghijklmnopqrs
84 ast.copy_location(ast.Constant(node.value.value.replace("\n```", "\n")), node)
85 )
86 return node 1eabcfdghijklmnopqrs
89def update_docstring(module): 1atbcud
90 from functools import reduce 1eabcfdghivjklmnopqrs
92 module.body = reduce(markdown_docstring, module.body, []) 1eabcfdghivjklmnopqrs
93 return TestStrings().visit(module) 1eabcfdghivjklmnopqrs
96docstring_ast_types = ast.ClassDef, ast.FunctionDef 1atbcud
97try: 1atbcud
98 docstring_ast_types += (ast.AsyncFunctionDef,) 1atbcud
99except:
100 ...
103def markdown_docstring(nodes, node): 1atbcud
104 if ( 1eabcfdghivjklmnopqrs
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()) 1eabcfdghijklmnopqr
111 return nodes.append(node) or nodes 1eabcfdghivjklmnopqrs
114def str_expr(node): 1atbcud
115 return isinstance(node, ast.Expr) and isinstance(node.value, ast.Constant) 1eabcfdghijklmnopqrs