Skip to content

parser

A simple class to provide access to full extent of statements with starting and ending lines.

closest_after(tree, lineno)

Return the closest item strictly before lineno.

Parameters:

Name Type Description Default
tree IntervalTree

The intervals obtained from get_stmt_ranges function.

required
lineno int

The lineno to use in the file.

required

Returns:

Type Description
Optional[Interval]

Optional[Interval]: An interval representing closest statement after the line if there is a statement after.

Source code in mkreports/parser.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def closest_after(tree: IntervalTree, lineno: int) -> Optional[Interval]:
    """
    Return the closest item strictly before lineno.

    Args:
        tree (IntervalTree): The intervals obtained from *get_stmt_ranges* function.
        lineno (int): The lineno to use in the file.

    Returns:
        Optional[Interval]: An interval representing closest statement after the line
            if there is a statement after.

    """
    tree_list = list(tree.items())
    # sort by size of element; this will be retained in later sorts
    tree_list.sort(key=lambda x: x.end - x.begin)
    # here we filter by those that come before and
    # sort by the difference to the current line
    # rest by difference to current line
    after_list = [x for x in tree_list if (x.begin > lineno)]
    after_list.sort(key=lambda x: x.begin - lineno)
    if len(after_list) > 0:
        return after_list[0]
    else:
        return None

closest_before(tree, lineno)

Return the closest item strictly before lineno.

Parameters:

Name Type Description Default
tree IntervalTree

The intervals obtained from get_stmt_ranges function.

required
lineno int

The lineno to use in the file.

required

Returns:

Type Description
Optional[Interval]

Optional[Interval]: An interval representing closest statement before the line if there is a statement before.

Source code in mkreports/parser.py
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
def closest_before(tree: IntervalTree, lineno: int) -> Optional[Interval]:
    """
    Return the closest item strictly before lineno.

    Args:
        tree (IntervalTree): The intervals obtained from *get_stmt_ranges* function.
        lineno (int): The lineno to use in the file.

    Returns:
        Optional[Interval]: An interval representing closest statement before the line
            if there is a statement before.

    """
    tree_list = list(tree.items())
    # sort by size of element; this will be retained in later sorts
    tree_list.sort(key=lambda x: x.end - x.begin)
    # here we filter by those that come before and
    # sort by the difference to the current line
    before_list = [x for x in tree_list if (x.begin < lineno)]
    before_list.sort(key=lambda x: lineno - x.begin)
    if len(before_list) > 0:
        return before_list[0]
    else:
        return None

envelope(tree, pos)

Interval that covers the given interval (i.e. is larger).

Parameters:

Name Type Description Default
tree IntervalTree

The intervals obtained from get_stmt_ranges function.

required
pos Interval

Interval to cover.

required

Returns:

Type Description
Optional[Interval]

Optional[Interval]: The next largest interval covering the current one, if there is one, otherwise None.

Source code in mkreports/parser.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
def envelope(tree: IntervalTree, pos: Interval) -> Optional[Interval]:
    """
    Interval that covers the given interval (i.e. is larger).

    Args:
        tree (IntervalTree): The intervals obtained from *get_stmt_ranges* function.
        pos (Interval): Interval to cover.

    Returns:
        Optional[Interval]: The next largest interval covering the current one,
            if there is one, otherwise None.

    """
    tree_list = list(tree.envelop(pos.begin - 1, pos.end))
    if len(tree_list) == 0:
        return None
    else:
        tree_list.sort(key=lambda x: x.end - x.begin)
        return tree_list[0]

get_neighbors(tree, lineno)

For a given lineno, get the current statement (if there is one), as well as the previous and next statements in the tree.

Parameters:

Name Type Description Default
tree IntervalTree required
lineno int required

Returns:

Type Description
Optional[Interval], Optional[Interval], Optional[Interval]

Interval of statement before the line, covering the current line and the next statement.

Source code in mkreports/parser.py
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
def get_neighbors(
    tree: IntervalTree, lineno: int
) -> Tuple[Optional[Interval], Optional[Interval], Optional[Interval]]:
    """
    For a given lineno, get the current statement (if there is one), as well
    as the previous and next statements in the tree.

    Args:
        tree (IntervalTree):
        lineno (int):

    Returns:
        (Optional[Interval], Optional[Interval], Optional[Interval]):
            Interval of statement before the line, covering the current
            line and the next statement.
    """
    return (
        closest_before(tree, lineno),
        smallest_overlap(tree, lineno),
        closest_after(tree, lineno),
    )

get_stmt_ranges(pyfile)

Parse the python file and return the ranges of all statements.

Here, we only return the range of the statements at the lowest level. This is to make it easier to find the 'previous' statement. The line numbers in the interval tree will be 1-based.

Parameters:

Name Type Description Default
pyfile Path

Path to the python file to analyze.

required

Returns:

Name Type Description
IntervallTree IntervalTree

An object representing the hierarchical intervals of the statements in the file.

Source code in mkreports/parser.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
def get_stmt_ranges(pyfile: Path) -> IntervalTree:
    """
    Parse the python file and return the ranges of all statements.

    Here, we only return the range of the statements at the lowest level.
    This is to make it easier to find the 'previous' statement.
    The line numbers in the interval tree will be 1-based.

    Args:
        pyfile (Path): Path to the python file to analyze.

    Returns:
        IntervallTree: An object representing the hierarchical intervals of the
            statements in the file.
    """
    # first we parse the python file into an AST
    with pyfile.open("r") as f:
        file_ast = ast.parse(f.read())
    inttree = IntervalTree()

    # now we want to walk along the tree and get the line extent of
    # all nodes that are statements; as data payload we attach
    # the parsed nodes
    for node in ast.walk(file_ast):
        if isinstance(node, ast.stmt):
            if node.lineno is not None and node.end_lineno is not None:
                inttree.add(
                    Interval(begin=node.lineno, end=node.end_lineno + 1, data=node)
                )

    return inttree

smallest_overlap(tree, lineno)

Find the closest match that overlaps and is shortests.

Parameters:

Name Type Description Default
tree IntervalTree

The intervals obtained from get_stmt_ranges function.

required
lineno int

The lineno to use in the file.

required

Returns:

Type Description
Optional[Interval]

Optional[Interval]: An interval if there is a statement at the line.

Source code in mkreports/parser.py
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
def smallest_overlap(tree: IntervalTree, lineno: int) -> Optional[Interval]:
    """
    Find the closest match that overlaps and is shortests.

    Args:
        tree (IntervalTree): The intervals obtained from *get_stmt_ranges* function.
        lineno (int): The lineno to use in the file.

    Returns:
        Optional[Interval]: An interval if there is a statement at the line.

    """
    overlap_set = tree.at(lineno)
    if len(overlap_set) > 0:
        # we take the shortest
        overlap_list = list(overlap_set)
        overlap_list.sort(key=lambda x: x.end - x.begin)
        return overlap_list[0]
    else:
        return None