From 0e4503fac43ad8e14303f5a7dd309922a12dfed4 Mon Sep 17 00:00:00 2001 From: Lars Bilke <lars.bilke@ufz.de> Date: Tue, 13 Jul 2021 16:02:05 +0200 Subject: [PATCH] User recipes, fixed Dockerfile output. --- README.md | 7 +- Test.ipynb | 301 ++++++++++++++++++++++++++++++++++++++++++++--- ogscm/cli.py | 28 +++-- ogscm/jupyter.py | 132 +++++++++++++++------ 4 files changed, 399 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index 7824cef..4174f91 100644 --- a/README.md +++ b/README.md @@ -248,14 +248,15 @@ def run_cmd(cmd): print(f"Executing: {cmd}") !poetry run {cmd} -out = setup_ui(run_cmd) +user_recipes = ["user_recipes/my_recipe.py"] # optional user recipes +out = setup_ui(run_cmd, user_recipes) ``` Options `--build` and `--convert` are enabeld per default, then click on button `CREATE CONTAINER`. Optionally run a second cell to get a download link to the image file: ```py -from ogscm.jupyter import display_download_link -display_download_link(out.outputs[0]['text']) +from ogscm.jupyter import display_download_link, display_source +display_source(display_download_link(out.outputs[0]['text']), "docker") ``` Output is stored in `[notebook-location]/_out`. UI state is preserved when notebook was saved. Enable recipes from the tab widget by clicking on the `Disabled`-button (`compiley.py`-recipe is enabled by default). diff --git a/Test.ipynb b/Test.ipynb index e181fe5..7c4f2dd 100644 --- a/Test.ipynb +++ b/Test.ipynb @@ -16,7 +16,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "e601a6cea8254f17ad460fb58473e517", + "model_id": "b5a18f4a9edf47429f58796fa4dcf460", "version_major": 2, "version_minor": 0 }, @@ -37,7 +37,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "a92cb290669d4b71a950accb6dcbdf0c", + "model_id": "897d99046565461d98d2d2074ab6431b", "version_major": 2, "version_minor": 0 }, @@ -58,7 +58,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "eda21a831fec407fbd1a6ce66b663f10", + "model_id": "77c051ed9f7646ff900daa6a719635a8", "version_major": 2, "version_minor": 0 }, @@ -72,7 +72,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "d6e9df62ad8d448ea627e76f349d1e8e", + "model_id": "44a8aaedf2f04721b00bc14174e26ebd", "version_major": 2, "version_minor": 0 }, @@ -86,7 +86,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "e8c6b62d436c4e9c9acb12dcd5d09f50", + "model_id": "aaaab6232b574919a3b6e177904967c2", "version_major": 2, "version_minor": 0 }, @@ -100,7 +100,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "2658d20285c9434dbbfdf6205b2d8a2b", + "model_id": "5a284ea048354fcba17f157b3c8fb4ba", "version_major": 2, "version_minor": 0 }, @@ -119,27 +119,290 @@ " print(f\"Executing: {cmd}\")\n", " !poetry run {cmd}\n", "\n", - "out = setup_ui(run_cmd)" + "user_recipes = [\"ogscm/user_recipes/foo.py\"]\n", + "out = setup_ui(run_cmd, user_recipes)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "id": "b9c0249b-f350-4a0f-b903-cc4354c4c89b", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Download container:\n" + ] + }, + { + "data": { + "text/html": [ + "<a href='_out/images/gcc-default-foo-default_value-7a6be99e36c6.sif' download=gcc-default-foo-default_value-7a6be99e36c6.sif>gcc-default-foo-default_value-7a6be99e36c6.sif</a><br>" + ], + "text/plain": [ + "/home/bilke/code/container-maker/_out/images/gcc-default-foo-default_value-7a6be99e36c6.sif" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "<style>pre { line-height: 125%; }\n", + "td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }\n", + "span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }\n", + "td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }\n", + "span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }\n", + ".output_html .hll { background-color: #ffffcc }\n", + ".output_html { background: #f8f8f8; }\n", + ".output_html .c { color: #408080; font-style: italic } /* Comment */\n", + ".output_html .err { border: 1px solid #FF0000 } /* Error */\n", + ".output_html .k { color: #008000; font-weight: bold } /* Keyword */\n", + ".output_html .o { color: #666666 } /* Operator */\n", + ".output_html .ch { color: #408080; font-style: italic } /* Comment.Hashbang */\n", + ".output_html .cm { color: #408080; font-style: italic } /* Comment.Multiline */\n", + ".output_html .cp { color: #BC7A00 } /* Comment.Preproc */\n", + ".output_html .cpf { color: #408080; font-style: italic } /* Comment.PreprocFile */\n", + ".output_html .c1 { color: #408080; font-style: italic } /* Comment.Single */\n", + ".output_html .cs { color: #408080; font-style: italic } /* Comment.Special */\n", + ".output_html .gd { color: #A00000 } /* Generic.Deleted */\n", + ".output_html .ge { font-style: italic } /* Generic.Emph */\n", + ".output_html .gr { color: #FF0000 } /* Generic.Error */\n", + ".output_html .gh { color: #000080; font-weight: bold } /* Generic.Heading */\n", + ".output_html .gi { color: #00A000 } /* Generic.Inserted */\n", + ".output_html .go { color: #888888 } /* Generic.Output */\n", + ".output_html .gp { color: #000080; font-weight: bold } /* Generic.Prompt */\n", + ".output_html .gs { font-weight: bold } /* Generic.Strong */\n", + ".output_html .gu { color: #800080; font-weight: bold } /* Generic.Subheading */\n", + ".output_html .gt { color: #0044DD } /* Generic.Traceback */\n", + ".output_html .kc { color: #008000; font-weight: bold } /* Keyword.Constant */\n", + ".output_html .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */\n", + ".output_html .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */\n", + ".output_html .kp { color: #008000 } /* Keyword.Pseudo */\n", + ".output_html .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */\n", + ".output_html .kt { color: #B00040 } /* Keyword.Type */\n", + ".output_html .m { color: #666666 } /* Literal.Number */\n", + ".output_html .s { color: #BA2121 } /* Literal.String */\n", + ".output_html .na { color: #7D9029 } /* Name.Attribute */\n", + ".output_html .nb { color: #008000 } /* Name.Builtin */\n", + ".output_html .nc { color: #0000FF; font-weight: bold } /* Name.Class */\n", + ".output_html .no { color: #880000 } /* Name.Constant */\n", + ".output_html .nd { color: #AA22FF } /* Name.Decorator */\n", + ".output_html .ni { color: #999999; font-weight: bold } /* Name.Entity */\n", + ".output_html .ne { color: #D2413A; font-weight: bold } /* Name.Exception */\n", + ".output_html .nf { color: #0000FF } /* Name.Function */\n", + ".output_html .nl { color: #A0A000 } /* Name.Label */\n", + ".output_html .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */\n", + ".output_html .nt { color: #008000; font-weight: bold } /* Name.Tag */\n", + ".output_html .nv { color: #19177C } /* Name.Variable */\n", + ".output_html .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */\n", + ".output_html .w { color: #bbbbbb } /* Text.Whitespace */\n", + ".output_html .mb { color: #666666 } /* Literal.Number.Bin */\n", + ".output_html .mf { color: #666666 } /* Literal.Number.Float */\n", + ".output_html .mh { color: #666666 } /* Literal.Number.Hex */\n", + ".output_html .mi { color: #666666 } /* Literal.Number.Integer */\n", + ".output_html .mo { color: #666666 } /* Literal.Number.Oct */\n", + ".output_html .sa { color: #BA2121 } /* Literal.String.Affix */\n", + ".output_html .sb { color: #BA2121 } /* Literal.String.Backtick */\n", + ".output_html .sc { color: #BA2121 } /* Literal.String.Char */\n", + ".output_html .dl { color: #BA2121 } /* Literal.String.Delimiter */\n", + ".output_html .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */\n", + ".output_html .s2 { color: #BA2121 } /* Literal.String.Double */\n", + ".output_html .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */\n", + ".output_html .sh { color: #BA2121 } /* Literal.String.Heredoc */\n", + ".output_html .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */\n", + ".output_html .sx { color: #008000 } /* Literal.String.Other */\n", + ".output_html .sr { color: #BB6688 } /* Literal.String.Regex */\n", + ".output_html .s1 { color: #BA2121 } /* Literal.String.Single */\n", + ".output_html .ss { color: #19177C } /* Literal.String.Symbol */\n", + ".output_html .bp { color: #008000 } /* Name.Builtin.Pseudo */\n", + ".output_html .fm { color: #0000FF } /* Name.Function.Magic */\n", + ".output_html .vc { color: #19177C } /* Name.Variable.Class */\n", + ".output_html .vg { color: #19177C } /* Name.Variable.Global */\n", + ".output_html .vi { color: #19177C } /* Name.Variable.Instance */\n", + ".output_html .vm { color: #19177C } /* Name.Variable.Magic */\n", + ".output_html .il { color: #666666 } /* Literal.Number.Integer.Long */\n", + "pre { line-height: 125%; }\n", + "td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }\n", + "span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }\n", + "td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }\n", + "span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }\n", + ".jp-RenderedHTML .hll { background-color: #ffffcc }\n", + ".jp-RenderedHTML { background: #f8f8f8; }\n", + ".jp-RenderedHTML .c { color: #408080; font-style: italic } /* Comment */\n", + ".jp-RenderedHTML .err { border: 1px solid #FF0000 } /* Error */\n", + ".jp-RenderedHTML .k { color: #008000; font-weight: bold } /* Keyword */\n", + ".jp-RenderedHTML .o { color: #666666 } /* Operator */\n", + ".jp-RenderedHTML .ch { color: #408080; font-style: italic } /* Comment.Hashbang */\n", + ".jp-RenderedHTML .cm { color: #408080; font-style: italic } /* Comment.Multiline */\n", + ".jp-RenderedHTML .cp { color: #BC7A00 } /* Comment.Preproc */\n", + ".jp-RenderedHTML .cpf { color: #408080; font-style: italic } /* Comment.PreprocFile */\n", + ".jp-RenderedHTML .c1 { color: #408080; font-style: italic } /* Comment.Single */\n", + ".jp-RenderedHTML .cs { color: #408080; font-style: italic } /* Comment.Special */\n", + ".jp-RenderedHTML .gd { color: #A00000 } /* Generic.Deleted */\n", + ".jp-RenderedHTML .ge { font-style: italic } /* Generic.Emph */\n", + ".jp-RenderedHTML .gr { color: #FF0000 } /* Generic.Error */\n", + ".jp-RenderedHTML .gh { color: #000080; font-weight: bold } /* Generic.Heading */\n", + ".jp-RenderedHTML .gi { color: #00A000 } /* Generic.Inserted */\n", + ".jp-RenderedHTML .go { color: #888888 } /* Generic.Output */\n", + ".jp-RenderedHTML .gp { color: #000080; font-weight: bold } /* Generic.Prompt */\n", + ".jp-RenderedHTML .gs { font-weight: bold } /* Generic.Strong */\n", + ".jp-RenderedHTML .gu { color: #800080; font-weight: bold } /* Generic.Subheading */\n", + ".jp-RenderedHTML .gt { color: #0044DD } /* Generic.Traceback */\n", + ".jp-RenderedHTML .kc { color: #008000; font-weight: bold } /* Keyword.Constant */\n", + ".jp-RenderedHTML .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */\n", + ".jp-RenderedHTML .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */\n", + ".jp-RenderedHTML .kp { color: #008000 } /* Keyword.Pseudo */\n", + ".jp-RenderedHTML .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */\n", + ".jp-RenderedHTML .kt { color: #B00040 } /* Keyword.Type */\n", + ".jp-RenderedHTML .m { color: #666666 } /* Literal.Number */\n", + ".jp-RenderedHTML .s { color: #BA2121 } /* Literal.String */\n", + ".jp-RenderedHTML .na { color: #7D9029 } /* Name.Attribute */\n", + ".jp-RenderedHTML .nb { color: #008000 } /* Name.Builtin */\n", + ".jp-RenderedHTML .nc { color: #0000FF; font-weight: bold } /* Name.Class */\n", + ".jp-RenderedHTML .no { color: #880000 } /* Name.Constant */\n", + ".jp-RenderedHTML .nd { color: #AA22FF } /* Name.Decorator */\n", + ".jp-RenderedHTML .ni { color: #999999; font-weight: bold } /* Name.Entity */\n", + ".jp-RenderedHTML .ne { color: #D2413A; font-weight: bold } /* Name.Exception */\n", + ".jp-RenderedHTML .nf { color: #0000FF } /* Name.Function */\n", + ".jp-RenderedHTML .nl { color: #A0A000 } /* Name.Label */\n", + ".jp-RenderedHTML .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */\n", + ".jp-RenderedHTML .nt { color: #008000; font-weight: bold } /* Name.Tag */\n", + ".jp-RenderedHTML .nv { color: #19177C } /* Name.Variable */\n", + ".jp-RenderedHTML .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */\n", + ".jp-RenderedHTML .w { color: #bbbbbb } /* Text.Whitespace */\n", + ".jp-RenderedHTML .mb { color: #666666 } /* Literal.Number.Bin */\n", + ".jp-RenderedHTML .mf { color: #666666 } /* Literal.Number.Float */\n", + ".jp-RenderedHTML .mh { color: #666666 } /* Literal.Number.Hex */\n", + ".jp-RenderedHTML .mi { color: #666666 } /* Literal.Number.Integer */\n", + ".jp-RenderedHTML .mo { color: #666666 } /* Literal.Number.Oct */\n", + ".jp-RenderedHTML .sa { color: #BA2121 } /* Literal.String.Affix */\n", + ".jp-RenderedHTML .sb { color: #BA2121 } /* Literal.String.Backtick */\n", + ".jp-RenderedHTML .sc { color: #BA2121 } /* Literal.String.Char */\n", + ".jp-RenderedHTML .dl { color: #BA2121 } /* Literal.String.Delimiter */\n", + ".jp-RenderedHTML .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */\n", + ".jp-RenderedHTML .s2 { color: #BA2121 } /* Literal.String.Double */\n", + ".jp-RenderedHTML .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */\n", + ".jp-RenderedHTML .sh { color: #BA2121 } /* Literal.String.Heredoc */\n", + ".jp-RenderedHTML .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */\n", + ".jp-RenderedHTML .sx { color: #008000 } /* Literal.String.Other */\n", + ".jp-RenderedHTML .sr { color: #BB6688 } /* Literal.String.Regex */\n", + ".jp-RenderedHTML .s1 { color: #BA2121 } /* Literal.String.Single */\n", + ".jp-RenderedHTML .ss { color: #19177C } /* Literal.String.Symbol */\n", + ".jp-RenderedHTML .bp { color: #008000 } /* Name.Builtin.Pseudo */\n", + ".jp-RenderedHTML .fm { color: #0000FF } /* Name.Function.Magic */\n", + ".jp-RenderedHTML .vc { color: #19177C } /* Name.Variable.Class */\n", + ".jp-RenderedHTML .vg { color: #19177C } /* Name.Variable.Global */\n", + ".jp-RenderedHTML .vi { color: #19177C } /* Name.Variable.Instance */\n", + ".jp-RenderedHTML .vm { color: #19177C } /* Name.Variable.Magic */\n", + ".jp-RenderedHTML .il { color: #666666 } /* Literal.Number.Integer.Long */</style><div class=\"highlight\"><pre><span></span><span class=\"c\"># syntax=docker/dockerfile:experimental</span>\n", + "\n", + "<span class=\"k\">FROM</span> <span class=\"s\">ubuntu:20.04</span> <span class=\"k\">AS</span> <span class=\"s\">build</span>\n", + "\n", + "<span class=\"c\"># Generated with ogs-container-maker 2.0.0</span>\n", + "\n", + "<span class=\"k\">RUN</span> apt-get update -y <span class=\"o\">&&</span> <span class=\"se\">\\</span>\n", + " <span class=\"nv\">DEBIAN_FRONTEND</span><span class=\"o\">=</span>noninteractive apt-get install -y --no-install-recommends <span class=\"se\">\\</span>\n", + " curl <span class=\"se\">\\</span>\n", + " make <span class=\"se\">\\</span>\n", + " tar <span class=\"se\">\\</span>\n", + " unzip <span class=\"se\">\\</span>\n", + " wget <span class=\"o\">&&</span> <span class=\"se\">\\</span>\n", + " rm -rf /var/lib/apt/lists/*\n", + "\n", + "<span class=\"c\"># --- Begin compiler.py ---</span>\n", + "\n", + "<span class=\"c\"># GNU compiler</span>\n", + "<span class=\"k\">RUN</span> apt-get update -y <span class=\"o\">&&</span> <span class=\"se\">\\</span>\n", + " <span class=\"nv\">DEBIAN_FRONTEND</span><span class=\"o\">=</span>noninteractive apt-get install -y --no-install-recommends <span class=\"se\">\\</span>\n", + " g++ <span class=\"se\">\\</span>\n", + " gcc <span class=\"o\">&&</span> <span class=\"se\">\\</span>\n", + " rm -rf /var/lib/apt/lists/*\n", + "\n", + "<span class=\"c\"># --- End compiler.py ---</span>\n", + "\n", + "<span class=\"c\"># Begin ogscm/user_recipes/foo.py</span>\n", + "\n", + "<span class=\"c\"># --- End ogscm/user_recipes/foo.py ---</span>\n", + "</pre></div>\n" + ], + "text/latex": [ + "\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n", + "\\PY{c}{\\PYZsh{} syntax=docker/dockerfile:experimental}\n", + "\n", + "\\PY{k}{FROM} \\PY{l+s}{ubuntu:20.04} \\PY{k}{AS} \\PY{l+s}{build}\n", + "\n", + "\\PY{c}{\\PYZsh{} Generated with ogs\\PYZhy{}container\\PYZhy{}maker 2.0.0}\n", + "\n", + "\\PY{k}{RUN} apt\\PYZhy{}get update \\PYZhy{}y \\PY{o}{\\PYZam{}\\PYZam{}} \\PY{l+s+se}{\\PYZbs{}}\n", + " \\PY{n+nv}{DEBIAN\\PYZus{}FRONTEND}\\PY{o}{=}noninteractive apt\\PYZhy{}get install \\PYZhy{}y \\PYZhy{}\\PYZhy{}no\\PYZhy{}install\\PYZhy{}recommends \\PY{l+s+se}{\\PYZbs{}}\n", + " curl \\PY{l+s+se}{\\PYZbs{}}\n", + " make \\PY{l+s+se}{\\PYZbs{}}\n", + " tar \\PY{l+s+se}{\\PYZbs{}}\n", + " unzip \\PY{l+s+se}{\\PYZbs{}}\n", + " wget \\PY{o}{\\PYZam{}\\PYZam{}} \\PY{l+s+se}{\\PYZbs{}}\n", + " rm \\PYZhy{}rf /var/lib/apt/lists/*\n", + "\n", + "\\PY{c}{\\PYZsh{} \\PYZhy{}\\PYZhy{}\\PYZhy{} Begin compiler.py \\PYZhy{}\\PYZhy{}\\PYZhy{}}\n", + "\n", + "\\PY{c}{\\PYZsh{} GNU compiler}\n", + "\\PY{k}{RUN} apt\\PYZhy{}get update \\PYZhy{}y \\PY{o}{\\PYZam{}\\PYZam{}} \\PY{l+s+se}{\\PYZbs{}}\n", + " \\PY{n+nv}{DEBIAN\\PYZus{}FRONTEND}\\PY{o}{=}noninteractive apt\\PYZhy{}get install \\PYZhy{}y \\PYZhy{}\\PYZhy{}no\\PYZhy{}install\\PYZhy{}recommends \\PY{l+s+se}{\\PYZbs{}}\n", + " g++ \\PY{l+s+se}{\\PYZbs{}}\n", + " gcc \\PY{o}{\\PYZam{}\\PYZam{}} \\PY{l+s+se}{\\PYZbs{}}\n", + " rm \\PYZhy{}rf /var/lib/apt/lists/*\n", + "\n", + "\\PY{c}{\\PYZsh{} \\PYZhy{}\\PYZhy{}\\PYZhy{} End compiler.py \\PYZhy{}\\PYZhy{}\\PYZhy{}}\n", + "\n", + "\\PY{c}{\\PYZsh{} Begin ogscm/user\\PYZus{}recipes/foo.py}\n", + "\n", + "\\PY{c}{\\PYZsh{} \\PYZhy{}\\PYZhy{}\\PYZhy{} End ogscm/user\\PYZus{}recipes/foo.py \\PYZhy{}\\PYZhy{}\\PYZhy{}}\n", + "\\end{Verbatim}\n" + ], + "text/plain": [ + "# syntax=docker/dockerfile:experimental\n", + "\n", + "FROM ubuntu:20.04 AS build\n", + "\n", + "# Generated with ogs-container-maker 2.0.0\n", + "\n", + "RUN apt-get update -y && \\\n", + " DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \\\n", + " curl \\\n", + " make \\\n", + " tar \\\n", + " unzip \\\n", + " wget && \\\n", + " rm -rf /var/lib/apt/lists/*\n", + "\n", + "# --- Begin compiler.py ---\n", + "\n", + "# GNU compiler\n", + "RUN apt-get update -y && \\\n", + " DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \\\n", + " g++ \\\n", + " gcc && \\\n", + " rm -rf /var/lib/apt/lists/*\n", + "\n", + "# --- End compiler.py ---\n", + "\n", + "# Begin ogscm/user_recipes/foo.py\n", + "\n", + "# --- End ogscm/user_recipes/foo.py ---" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "from ogscm.jupyter import display_download_link\n", - "display_download_link(out.outputs[0]['text'])" + "from ogscm.jupyter import display_download_link, display_source\n", + "display_source(display_download_link(out.outputs[0]['text']), \"docker\")" ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a5189654-2d4d-4f6f-a779-0d9c3d4e1846", - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/ogscm/cli.py b/ogscm/cli.py index 4391b89..2a5e08a 100644 --- a/ogscm/cli.py +++ b/ogscm/cli.py @@ -64,21 +64,23 @@ def main(): # pragma: no cover # https://stackoverflow.com/a/1463370/80480 ldict = {"filename": recipe} - try: - recipe_builtin = pkg_resources.read_text(recipes, recipe) - exec(compile(recipe_builtin, recipe, "exec"), locals(), ldict) - except Exception as err: - error_class = err.__class__.__name__ - detail = err.args[0] - cl, exc, tb = sys.exc_info() - line_number = traceback.extract_tb(tb)[-1][1] - print(f"{error_class} at line {line_number}: {detail}") - if not os.path.exists(recipe): - print(f"{recipe} does not exist!") - exit(1) - + if os.path.exists(recipe): with open(recipe, "r") as reader: exec(compile(reader.read(), recipe, "exec"), locals(), ldict) + else: + try: + recipe_builtin = pkg_resources.read_text(recipes, recipe) + exec(compile(recipe_builtin, recipe, "exec"), locals(), ldict) + except Exception as err: + error_class = err.__class__.__name__ + detail = err.args[0] + cl, exc, tb = sys.exc_info() + line_number = traceback.extract_tb(tb)[-1][1] + print(f"{error_class} at line {line_number}: {detail}") + if not os.path.exists(recipe): + print(f"{recipe} does not exist!") + exit(1) + if "out_dir" in ldict: out_dir = ldict["out_dir"] if "toolchain" in ldict: diff --git a/ogscm/jupyter.py b/ogscm/jupyter.py index 8cf5816..0e543a2 100644 --- a/ogscm/jupyter.py +++ b/ogscm/jupyter.py @@ -4,18 +4,20 @@ import os import pathlib import importlib.resources as pkg_resources + def on_button_recipe_clicked(b): # print(b) - if b['new']: + if b["new"]: b.owner.icon = "check" b.owner.description = "Enabled" - b.owner.button_style='success' + b.owner.button_style = "success" else: b.owner.icon = "" b.owner.description = "Disabled" - b.owner.button_style='' + b.owner.button_style = "" + -def setup_ui(run_cmd): +def setup_ui(run_cmd, user_recipes=[]): from ogscm.args import setup_args_parser from ogscm import recipes import ipywidgets as widgets @@ -24,36 +26,50 @@ def setup_ui(run_cmd): args_dict = {} for group in parser._action_groups: - if group.title in ['positional arguments', 'optional arguments', 'Image deployment', 'Maintenance']: + if group.title in [ + "positional arguments", + "optional arguments", + "Image deployment", + "Maintenance", + ]: continue print(group.title) display(setup_args(group._group_actions, args_dict)) dirname = pathlib.Path(__file__).parent.parent / "ogscm" / "recipes" + recipes = sorted(os.listdir(dirname)) + for user_recipe in user_recipes: + if os.path.exists(user_recipe): + recipes.append(user_recipe) tab = widgets.Tab() tabs = [] tab_names = [] - for filename in sorted(os.listdir(dirname)): + for filename in recipes: if filename.endswith(".py") and not filename.startswith("_"): parser = argparse.ArgumentParser(add_help=False) ldict = {"filename": filename} execute = False - recipe_builtin = pkg_resources.read_text(recipes, filename) - exec(compile(recipe_builtin, filename, "exec"), locals(), ldict) + try: + recipe_builtin = pkg_resources.read_text(recipes, filename) + exec(compile(recipe_builtin, filename, "exec"), locals(), ldict) + except Exception as err: + if os.path.exists(filename): + with open(filename, "r") as reader: + exec(compile(reader.read(), filename, "exec"), locals(), ldict) if filename == "compiler.py": button = widgets.ToggleButton( value=True, - description='Enabled', - button_style='success', - icon = "check" + description="Enabled", + button_style="success", + icon="check", ) else: button = widgets.ToggleButton( value=False, - description='Disabled', + description="Disabled", ) - button.observe(on_button_recipe_clicked, 'value') + button.observe(on_button_recipe_clicked, "value") args_dict[filename] = button grid = setup_args(parser._actions, args_dict) tabs.append(widgets.VBox(children=[button, grid])) @@ -64,17 +80,27 @@ def setup_ui(run_cmd): tab.set_title(idx, val) display(tab) - button = widgets.Button(description="CREATE CONTAINER", button_style="primary", layout=widgets.Layout(width='100%', height='35px')) + button = widgets.Button( + description="CREATE CONTAINER", + button_style="primary", + layout=widgets.Layout(width="100%", height="35px"), + ) global out - out = widgets.Output(layout={'border': '1px solid black'}) + out = widgets.Output(layout={"border": "1px solid black"}) display(button, out) - button.on_click(functools.partial(on_button_clicked, out=out, args_dict=args_dict, run_cmd=run_cmd)) + button.on_click( + functools.partial( + on_button_clicked, out=out, args_dict=args_dict, run_cmd=run_cmd + ) + ) return out + def setup_args(actions, args_dict): import ipywidgets as widgets + items = [] for arg in actions: name = arg.option_strings[0] @@ -88,19 +114,27 @@ def setup_args(actions, args_dict): default_value = "" if name == "--build_args": default_value = "\"'--progress=plain'\"" - widget = widgets.Text(value=default_value , placeholder=f"{arg.default}", description="(?)", description_tooltip=help) + widget = widgets.Text( + value=default_value, + placeholder=f"{arg.default}", + description="(?)", + description_tooltip=help, + ) items.append(widgets.Label(value=name)) items.append(widget) args_dict[name] = widget gridbox = widgets.GridBox( - children = items, - layout = widgets.Layout( - grid_template_columns='150px auto 150px auto', - grid_template_rows='auto', - grid_gap='10px 10px')) + children=items, + layout=widgets.Layout( + grid_template_columns="150px auto 150px auto", + grid_template_rows="auto", + grid_gap="10px 10px", + ), + ) return gridbox + def create_cli(items): cli_string = "" recipes = "" @@ -118,6 +152,7 @@ def create_cli(items): return f"{recipes}{cli_string}" + def on_button_clicked(b, out, args_dict, run_cmd): with out: out.clear_output() @@ -125,8 +160,11 @@ def on_button_clicked(b, out, args_dict, run_cmd): cmd = f"ogscm {cli_string}" run_cmd(cmd) + from IPython.display import FileLink import os + + class DownloadFileLink(FileLink): html_link_str = "<a href='{link}' download={file_name}>{link_text}</a>" @@ -138,10 +176,37 @@ class DownloadFileLink(FileLink): def _format_path(self): from html import escape - fp = ''.join([self.url_prefix, escape(self.path)]) - return ''.join([self.result_html_prefix, - self.html_link_str.format(link=fp, file_name=self.file_name, link_text=self.link_text), - self.result_html_suffix]) + + fp = "".join([self.url_prefix, escape(self.path)]) + return "".join( + [ + self.result_html_prefix, + self.html_link_str.format( + link=fp, file_name=self.file_name, link_text=self.link_text + ), + self.result_html_suffix, + ] + ) + + +def display_source(code, language): + import IPython + + def _jupyterlab_repr_html_(self): + from pygments import highlight + from pygments.formatters import HtmlFormatter + + fmt = HtmlFormatter() + style = "<style>{}\n{}</style>".format( + fmt.get_style_defs(".output_html"), fmt.get_style_defs(".jp-RenderedHTML") + ) + return style + highlight(self.data, self._get_lexer(), fmt) + + # Replace _repr_html_ with our own version that adds the 'jp-RenderedHTML' class + # in addition to 'output_html'. + IPython.display.Code._repr_html_ = _jupyterlab_repr_html_ + return IPython.display.Code(data=code, language=language) + def display_download_link(output): from pygments import highlight @@ -151,24 +216,23 @@ def display_download_link(output): import re # Sif - p = re.compile('.*Build.*: (.*\.sif)') + p = re.compile(".*Build.*: (.*\.sif)") m = p.search(output) - print("Download container:") if m: sif_file = m.group(1) from ogscm.jupyter import DownloadFileLink import os + + print("Download container:") display(DownloadFileLink(os.path.relpath(sif_file))) # Dockerfile - p = re.compile('Created definition (.*)') + p = re.compile("Created definition (.*)") m = p.search(output) dockerfile = m.group(1) with open(dockerfile) as f: code = f.read() - - formatter = HtmlFormatter() - IPython.display.HTML('<style type="text/css">{}</style>{}'.format( - formatter.get_style_defs('.highlight'), - highlight(code, DockerLexer(), formatter))) + # Does not work? + # display_source(code, "python3") + return code -- GitLab