Skip to content

Commit

Permalink
[cards] PythonCode component
Browse files Browse the repository at this point in the history
- Metaflow Card component for rendering a syntax highlighed python function
- Example flow :

```
from metaflow import FlowSpec, step, card, project
from metaflow.cards import PythonCode

class PythonCodeDemoFlow(FlowSpec):
    """
    A simple flow demonstrating the PythonCode card component in Metaflow.
    """

    @card(type="blank", id="code_display")
    @step
    def start(self):
        # Display the code of this very method using PythonCode
        code_display = PythonCode(
            getattr(PythonCodeDemoFlow, "start")
        )
        current.card.append(code_display)
        self.next(self.end)

    @step
    def end(self):
        pass

if __name__ == '__main__':
    PythonCodeDemoFlow()
```
  • Loading branch information
valayDave committed Jan 6, 2025
1 parent 950428e commit fe04de4
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 27 deletions.
1 change: 1 addition & 0 deletions metaflow/cards.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
Markdown,
VegaChart,
ProgressBar,
PythonCode,
)
from metaflow.plugins.cards.card_modules.basic import (
DefaultCard,
Expand Down
16 changes: 16 additions & 0 deletions metaflow/plugins/cards/card_modules/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,22 @@ def render(self):
return datadict


class PythonCodeComponent(DefaultComponent):

type = "pythonCode"

def __init__(self, data=None):
super().__init__(title=None, subtitle=None)
self._data = data

def render(self):
datadict = super().render()
datadict["data"] = self._data
if self.component_id is not None:
datadict["id"] = self.component_id
return datadict


class HTMLComponent(DefaultComponent):
type = "html"

Expand Down
38 changes: 38 additions & 0 deletions metaflow/plugins/cards/card_modules/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
ImageComponent,
SectionComponent,
MarkdownComponent,
PythonCodeComponent,
)
from .card import MetaflowCardComponent
from .convert_to_native_type import TaskToDict, _full_classname
from .renderer_tools import render_safely
import uuid
import inspect


def create_component_id(component):
Expand Down Expand Up @@ -823,3 +825,39 @@ def render(self):
if self._chart_inside_table and "autosize" not in self._spec:
data["spec"]["autosize"] = "fit-x"
return data


class PythonCode(UserComponent):
"""
A component to display Python code with syntax highlighting.
Example:
```python
@card
@step
def my_step(self):
def my_function():
x = 1
y = 2
return x + y
current.card.append(
PythonCode(my_function)
)
```
Parameters
----------
code_func : function
The function whose source code should be displayed.
"""

def __init__(self, code_func):
self._code_string = inspect.getsource(code_func)

@with_default_component_id
@render_safely
def render(self):
_code_component = PythonCodeComponent(self._code_string)
_code_component.component_id = self.component_id
return _code_component.render()
52 changes: 27 additions & 25 deletions metaflow/plugins/cards/card_modules/main.js

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import Text from "./text.svelte";
import Title from "./title.svelte";
import VegaChart from "./vega-chart.svelte";
import PythonCode from "./python-code.svelte";
export let componentData: types.CardComponent;
Expand All @@ -33,6 +34,7 @@
text: Text,
title: Title,
vegaChart: VegaChart,
pythonCode: PythonCode,
};
let component = typesMap?.[componentData.type];
Expand Down
19 changes: 19 additions & 0 deletions metaflow/plugins/cards/ui/src/components/python-code.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!-- This component will render a simple log with syntax highlighting -->
<script lang="ts">
import type * as types from "../types";
export let componentData: types.PythonCodeComponent;
let el: HTMLElement;
function highlightCode() {
el && (window as any)?.Prism?.highlightElement(el, );
}
$: el ? highlightCode() : null;
</script>

<!-- This needs to be in this exact format of <pre><code> ... without a new line between the <pre> and <code> tags -->
<!-- Need to do this to avoid weird indentation issues -->
<!-- based on https://github.com/PrismJS/prism/issues/554#issuecomment-83197995 -->
<pre data-component="pythonCode"><code class="language-python" bind:this={el}>{componentData.data}
</code>
</pre>
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import Markdown from "./markdown.svelte";
import Text from "./text.svelte";
import ProgressBar from "./progress-bar.svelte";
import PythonCode from "./python-code.svelte";
export let componentData: types.TableDataCell;
let component: ComponentType;
Expand All @@ -25,6 +26,7 @@
progressBar: ProgressBar,
text: Text,
vegaChart: VegaChart,
pythonCode: PythonCode,
};
const type = (componentData as types.CardComponent)?.type;
Expand Down
12 changes: 10 additions & 2 deletions metaflow/plugins/cards/ui/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ export type TableDataCell =
| MarkdownComponent
| ProgressBarComponent
| TextComponent
| VegaChartComponent;
| VegaChartComponent
| PythonCodeComponent;

export type TableColumns = string[];
export type TableData = TableDataCell[][];
Expand Down Expand Up @@ -179,6 +180,12 @@ export interface LogComponent {
data: string;
}

export interface PythonCodeComponent {
type: "pythonCode";
id?: string;
data: string;
}

export interface MarkdownComponent {
type: "markdown";
id?: string;
Expand Down Expand Up @@ -207,4 +214,5 @@ export type CardComponent =
| TableComponent
| TextComponent
| TitleComponent
| VegaChartComponent;
| VegaChartComponent
| PythonCodeComponent;

0 comments on commit fe04de4

Please sign in to comment.