Skip to content

tracker

CodeBlock

Source code in mkreports/tracker.py
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
@dataclass
class CodeBlock:
    """
    Structure representing information about a block of code.

    Args:
        filename (str): The name of the file with the code.
        co_name (str): Name of the code block
        line_start (int): Number of the line where the code block starts.
        line_end (int): Number of the line where the code block ends.
    """

    filename: str
    co_name: str
    line_start: int
    line_end: int

    def md_code(
        self, relative_to: Optional[Path] = None, name_only: bool = False
    ) -> Code:
        """
        Return a MdObj representing the code in the block.

        Args:
            relative_to (Optional[Path]): The path relative to which the title should be.
            name_only (bool): Should only the name of the code-file be used.

        Returns:
            Code: Code object with the code represented by the code block.

        """
        code = dedent(
            read_file(
                Path(self.filename),
                from_line=self.line_start,
                to_line=self.line_end,
            )
        )
        try:
            assert relative_to is not None
            filename_to_use = str(Path(self.filename).relative_to(relative_to))
        except Exception:
            if name_only:
                filename_to_use = Path(self.filename).name
            else:
                filename_to_use = self.filename

        return Code(
            code=code,
            title=filename_to_use,
            first_line=self.line_start,
            language="python",
        )

md_code(relative_to=None, name_only=False)

Return a MdObj representing the code in the block.

Parameters:

Name Type Description Default
relative_to Optional[Path]

The path relative to which the title should be.

None
name_only bool

Should only the name of the code-file be used.

False

Returns:

Name Type Description
Code Code

Code object with the code represented by the code block.

Source code in mkreports/tracker.py
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
def md_code(
    self, relative_to: Optional[Path] = None, name_only: bool = False
) -> Code:
    """
    Return a MdObj representing the code in the block.

    Args:
        relative_to (Optional[Path]): The path relative to which the title should be.
        name_only (bool): Should only the name of the code-file be used.

    Returns:
        Code: Code object with the code represented by the code block.

    """
    code = dedent(
        read_file(
            Path(self.filename),
            from_line=self.line_start,
            to_line=self.line_end,
        )
    )
    try:
        assert relative_to is not None
        filename_to_use = str(Path(self.filename).relative_to(relative_to))
    except Exception:
        if name_only:
            filename_to_use = Path(self.filename).name
        else:
            filename_to_use = self.filename

    return Code(
        code=code,
        title=filename_to_use,
        first_line=self.line_start,
        language="python",
    )

SimpleTracker

Bases: BaseTracker

Track first and last line of a code context.

When starting it records the line after the current statement, and stopping the line where the current statement ends.

The first and last line are required to be in the same file.

Source code in mkreports/tracker.py
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
class SimpleTracker(BaseTracker):
    """
    Track first and last line of a code context.

    When starting it records the line after the current
    statement, and stopping the line where the current statement ends.

    The first and last line are required to be in the same file.
    """

    def __init__(self):
        """Initialize the tracker."""
        self._active = False
        self.line_start = None
        self.line_end = None
        self.co_name = None

    def start(self, frame_info: inspect.FrameInfo) -> None:
        """
        Start the tracker.

        Args:
            frame_info (inspect.FrameInfo): A FrameInfo object of where the tracking starts.
        """
        if frame_info.filename == "<stdin>":
            raise CannotTrackError(f"Cannot track {frame_info.filename}")

        self.stmt_tree = parser.get_stmt_ranges(Path(frame_info.filename))
        stmt_after = parser.closest_after(self.stmt_tree, frame_info.lineno)
        self.filename = frame_info.filename
        if stmt_after is None:
            self.line_start = frame_info.lineno
        else:
            self.line_start = stmt_after.begin
        self.co_name = frame_info.frame.f_code.co_name
        self._active = True

    def stop(self, frame_info: inspect.FrameInfo) -> None:
        """
        Stop the tracker.

        Args:
            frame_info (inspect.FrameInfo): FraneInfo where the tracking ends.
        """
        if not self.active:
            raise TrackerNotActiveError("SimpleTracker not active")
        else:
            cur_stmt_lines = parser.smallest_overlap(self.stmt_tree, frame_info.lineno)
            if cur_stmt_lines is not None:
                self.line_end = cur_stmt_lines.end
            else:
                raise Exception("Could not find current statement")
        self._active = False

    def code(self) -> List[CodeBlock]:
        """
        Return the tracked code.

        Returns:
            List[CodeBlock]: List of code blocks. Here, the list is only of length 1.

        """
        if (
            self.active
            or self.line_start is None
            or self.line_end is None
            or self.co_name is None
        ):
            raise TrackerEmptyError()
        else:
            return [
                CodeBlock(self.filename, self.co_name, self.line_start, self.line_end)
            ]

    @property
    def active(self) -> bool:
        """

        Returns:
            bool: Is the tracker currently active?

        """
        return self._active

__init__()

Initialize the tracker.

Source code in mkreports/tracker.py
126
127
128
129
130
131
def __init__(self):
    """Initialize the tracker."""
    self._active = False
    self.line_start = None
    self.line_end = None
    self.co_name = None

active()

Returns:

Name Type Description
bool bool

Is the tracker currently active?

Source code in mkreports/tracker.py
190
191
192
193
194
195
196
197
198
@property
def active(self) -> bool:
    """

    Returns:
        bool: Is the tracker currently active?

    """
    return self._active

code()

Return the tracked code.

Returns:

Type Description
List[CodeBlock]

List[CodeBlock]: List of code blocks. Here, the list is only of length 1.

Source code in mkreports/tracker.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
def code(self) -> List[CodeBlock]:
    """
    Return the tracked code.

    Returns:
        List[CodeBlock]: List of code blocks. Here, the list is only of length 1.

    """
    if (
        self.active
        or self.line_start is None
        or self.line_end is None
        or self.co_name is None
    ):
        raise TrackerEmptyError()
    else:
        return [
            CodeBlock(self.filename, self.co_name, self.line_start, self.line_end)
        ]

start(frame_info)

Start the tracker.

Parameters:

Name Type Description Default
frame_info inspect.FrameInfo

A FrameInfo object of where the tracking starts.

required
Source code in mkreports/tracker.py
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
def start(self, frame_info: inspect.FrameInfo) -> None:
    """
    Start the tracker.

    Args:
        frame_info (inspect.FrameInfo): A FrameInfo object of where the tracking starts.
    """
    if frame_info.filename == "<stdin>":
        raise CannotTrackError(f"Cannot track {frame_info.filename}")

    self.stmt_tree = parser.get_stmt_ranges(Path(frame_info.filename))
    stmt_after = parser.closest_after(self.stmt_tree, frame_info.lineno)
    self.filename = frame_info.filename
    if stmt_after is None:
        self.line_start = frame_info.lineno
    else:
        self.line_start = stmt_after.begin
    self.co_name = frame_info.frame.f_code.co_name
    self._active = True

stop(frame_info)

Stop the tracker.

Parameters:

Name Type Description Default
frame_info inspect.FrameInfo

FraneInfo where the tracking ends.

required
Source code in mkreports/tracker.py
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
def stop(self, frame_info: inspect.FrameInfo) -> None:
    """
    Stop the tracker.

    Args:
        frame_info (inspect.FrameInfo): FraneInfo where the tracking ends.
    """
    if not self.active:
        raise TrackerNotActiveError("SimpleTracker not active")
    else:
        cur_stmt_lines = parser.smallest_overlap(self.stmt_tree, frame_info.lineno)
        if cur_stmt_lines is not None:
            self.line_end = cur_stmt_lines.end
        else:
            raise Exception("Could not find current statement")
    self._active = False

read_file(path, from_line=None, to_line=None)

Read a part of a file.

Reads a file from a line to a certain line. All line numbers are assumed to start with 0.

Parameters:

Name Type Description Default
path Path

Path to the code file.

required
from_line Optional[int]

Starting line.

None
to_line Optional[int]

Ending line.

None

Returns:

Name Type Description
Str str

String representing the code.

Source code in mkreports/tracker.py
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
def read_file(
    path: Path, from_line: Optional[int] = None, to_line: Optional[int] = None
) -> str:

    """
    Read a part of a file.

    Reads a file from a line to a certain line. All line numbers are assumed to
    start with 0.

    Args:
        path (Path): Path to the code file.
        from_line (Optional[int]): Starting line.
        to_line (Optional[int]): Ending line.

    Returns:
        Str: String representing the code.

    """
    with path.open("r") as f:
        lines = f.readlines()

    # the from_line to_line are line-numbers, not indices. to_line is included
    return "".join(
        lines[slice(from_line - 1 if from_line is not None else None, to_line, 1)]
    )