Skip to content

code_context

Module providing a class and context manager for tracking code.

The way of tracking code and displaying it is handled through a context manager, which is the return value obtained when adding an object to a page.

The context manager gives different options on how to display the code and the results such as:

  • A code block at the top followed by the output similar to standard jupyter notebooks.
  • A Tab-format where output and code are on separate tabs
  • A collapsed code block followed by the output or the output followed by a collapsed code block.

CodeContext

Context manager for the code tracking and content accumulation.

Source code in mkreports/code_context.py
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 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
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
class CodeContext:
    """
    Context manager for the code tracking and content accumulation.
    """

    tracker: BaseTracker

    def __init__(
        self,
        layout: Layouts,
        relative_to: Optional[Path] = None,
        name_only: bool = False,
        add_bottom: bool = True,
        stack_level: int = 2,
    ):
        """
        Initialize the context manager. This should usually not be needed by
        end users.

        Args:
            layout (Layouts): The layout to use. One of
                'tabbed', 'top-o', 'top-c', 'bottom-o', 'bottom-c' or 'nocode'.
            relative_to (Optional[Path]): Path relative to where the code file will be named.
            name_only (bool): For the code file, use name instead of path?
            add_bottom (bool): Should content be added to bottom or top of page?
            stack_level (int): Levels lower in the stack where the code is to be tracked.
        """
        self.layout: Layouts = layout
        self.do_tracking = layout != "nocode"
        self.tracker = SimpleTracker()
        self.stack_level = stack_level
        self.obj_list = []
        self.relative_to = relative_to
        self.add_bottom = add_bottom
        self.name_only = name_only
        self._active = False

    def __enter__(self) -> "CodeContext":
        if self.do_tracking:
            self.tracker.start(inspect.stack()[self.stack_level])
        self._active = True
        return self

    def __exit__(self, exc_type, exc_val, traceback) -> None:
        del exc_type, exc_val, traceback
        self._active = False
        if self.do_tracking:
            self.tracker.stop(inspect.stack()[self.stack_level])

    @property
    def active(self):
        """Indicates if the context-manager is active."""
        return self._active

    def add(self, md_obj: MdObj) -> None:
        """
        Add a new content object.

        Args:
            md_obj (MdObj): The content to be added.
        """
        if self.add_bottom:
            self.obj_list.append(md_obj)
        else:
            self.obj_list.insert(0, md_obj)

    def md_obj(self, page_info: PageInfo) -> MdObj:
        """
        Return the markdown object that represents output and code.

        Args:
            page_info (PageInfo): PageInfo object about the page where the
                content is to be added.

        Returns:
            MdObj: Markdown object representing the formatted output in the
                requested layout
        """
        content = MdSeq(self.obj_list)
        if self.layout == "nocode":
            code_final = None
        else:
            code_blocks = self.tracker.code()
            # turn code blocks into md
            code_md_list = [
                block.md_code(relative_to=self.relative_to, name_only=self.name_only)
                for block in code_blocks
            ]
            if len(code_md_list) > 1:
                # turn it into tabs
                code_final = Tab(code_md_list[0], title="<main>")
                for block, md_code in zip(code_blocks, code_md_list):
                    code_final += Tab(md_code, title=block.co_name)
            else:
                # just keep the code block as is
                code_final = code_md_list[0]

        return do_layout(
            code=code_final, content=content, page_info=page_info, layout=self.layout
        )

__init__(layout, relative_to=None, name_only=False, add_bottom=True, stack_level=2)

Initialize the context manager. This should usually not be needed by end users.

Parameters:

Name Type Description Default
layout Layouts

The layout to use. One of 'tabbed', 'top-o', 'top-c', 'bottom-o', 'bottom-c' or 'nocode'.

required
relative_to Optional[Path]

Path relative to where the code file will be named.

None
name_only bool

For the code file, use name instead of path?

False
add_bottom bool

Should content be added to bottom or top of page?

True
stack_level int

Levels lower in the stack where the code is to be tracked.

2
Source code in mkreports/code_context.py
 90
 91
 92
 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
118
def __init__(
    self,
    layout: Layouts,
    relative_to: Optional[Path] = None,
    name_only: bool = False,
    add_bottom: bool = True,
    stack_level: int = 2,
):
    """
    Initialize the context manager. This should usually not be needed by
    end users.

    Args:
        layout (Layouts): The layout to use. One of
            'tabbed', 'top-o', 'top-c', 'bottom-o', 'bottom-c' or 'nocode'.
        relative_to (Optional[Path]): Path relative to where the code file will be named.
        name_only (bool): For the code file, use name instead of path?
        add_bottom (bool): Should content be added to bottom or top of page?
        stack_level (int): Levels lower in the stack where the code is to be tracked.
    """
    self.layout: Layouts = layout
    self.do_tracking = layout != "nocode"
    self.tracker = SimpleTracker()
    self.stack_level = stack_level
    self.obj_list = []
    self.relative_to = relative_to
    self.add_bottom = add_bottom
    self.name_only = name_only
    self._active = False

active()

Indicates if the context-manager is active.

Source code in mkreports/code_context.py
132
133
134
135
@property
def active(self):
    """Indicates if the context-manager is active."""
    return self._active

add(md_obj)

Add a new content object.

Parameters:

Name Type Description Default
md_obj MdObj

The content to be added.

required
Source code in mkreports/code_context.py
137
138
139
140
141
142
143
144
145
146
147
def add(self, md_obj: MdObj) -> None:
    """
    Add a new content object.

    Args:
        md_obj (MdObj): The content to be added.
    """
    if self.add_bottom:
        self.obj_list.append(md_obj)
    else:
        self.obj_list.insert(0, md_obj)

md_obj(page_info)

Return the markdown object that represents output and code.

Parameters:

Name Type Description Default
page_info PageInfo

PageInfo object about the page where the content is to be added.

required

Returns:

Name Type Description
MdObj MdObj

Markdown object representing the formatted output in the requested layout

Source code in mkreports/code_context.py
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
def md_obj(self, page_info: PageInfo) -> MdObj:
    """
    Return the markdown object that represents output and code.

    Args:
        page_info (PageInfo): PageInfo object about the page where the
            content is to be added.

    Returns:
        MdObj: Markdown object representing the formatted output in the
            requested layout
    """
    content = MdSeq(self.obj_list)
    if self.layout == "nocode":
        code_final = None
    else:
        code_blocks = self.tracker.code()
        # turn code blocks into md
        code_md_list = [
            block.md_code(relative_to=self.relative_to, name_only=self.name_only)
            for block in code_blocks
        ]
        if len(code_md_list) > 1:
            # turn it into tabs
            code_final = Tab(code_md_list[0], title="<main>")
            for block, md_code in zip(code_blocks, code_md_list):
                code_final += Tab(md_code, title=block.co_name)
        else:
            # just keep the code block as is
            code_final = code_md_list[0]

    return do_layout(
        code=code_final, content=content, page_info=page_info, layout=self.layout
    )

do_layout(code, content, layout, page_info)

Do the layouting for a content and code block.

Parameters:

Name Type Description Default
code Optional[MdObj]

The MdObj for the code. If layout is 'nocode', can be None.

required
content MdObj

The content to add. Can't be missing.

required
layout Layouts

Type of layout for code-tracking. One of 'tabbed', 'top-o', 'top-c', 'bottom-o', 'bottom-c' or 'nocode'.

required
page_info PageInfo

PageInfo object corresponding to the page to which it should be added.

required

Returns:

Type Description
MdObj

A MdObj with the requested layout.

Source code in mkreports/code_context.py
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
def do_layout(
    code: Optional[MdObj], content: MdObj, layout: Layouts, page_info: PageInfo
) -> MdObj:
    """
    Do the layouting for a content and code block.

    Args:
        code (Optional[MdObj]): The MdObj for the code. If layout is 'nocode', can be None.
        content (MdObj): The content to add. Can't be missing.
        layout (Layouts): Type of layout for code-tracking. One of
                'tabbed', 'top-o', 'top-c', 'bottom-o', 'bottom-c' or 'nocode'.
        page_info (PageInfo): PageInfo object corresponding to the page to which it
            should be added.

    Returns:
        A MdObj with the requested layout.

    """
    if layout == "nocode":
        return content
    else:
        assert code is not None
        if layout == "top-c":
            return (
                Admonition(
                    code,
                    page_info=page_info,
                    collapse=True,
                    title="Code",
                    kind="code",
                )
                + content
                + HLine()
            )
        elif layout == "top-o":
            return code + content + HLine()
        elif layout == "bottom-c":
            return (
                content
                + Admonition(
                    code,
                    page_info=page_info,
                    collapse=True,
                    title="Code",
                    kind="code",
                )
                + HLine()
            )
        elif layout == "bottom-o":
            return content + code + HLine()
        elif layout == "tabbed":
            return Tab(content, title="Content") + Tab(code, title="Code") + HLine()
        else:
            raise Exception("Unknown layout type.")