Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Robolite #1

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .binder/environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ dependencies:
- ipyvue
- ipyvuetify
- plotly
- jupyterlab_robotmode >=0.3.1
### FEDERATED EXTENSIONS ###

# the chunk below gets copied from `../docs/environment.yml` by `doit env`
Expand Down
Binary file added app/kernelspecs/robotframework.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions app/lab/index.template.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const styles = import('./style.js');
const serverExtensions = [
import('@jupyterlite/javascript-kernel-extension'),
import('@jupyterlite/pyolite-kernel-extension'),
import('@jupyterlite/robolite-kernel-extension'),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this isn't necessary, as it's entirely possible to package up a lite kernel as a pip-installable, prebuilt extension... it's a much smaller webpack burden, for sure, which is always good.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bollwyvl Yes! The pip-packages are already hatching here https://github.com/robots-from-jupyter/robotkernel/tree/robolite/lite/jupyterlite-robotkernel/packages and will be merged into master once I'm ready.

You may recall that initially I just grepped everything pyolite and copied them to robolite to make robotkernel work :)

I still need this fork for the hack for Camunda integration, until I learn to make it otherwise: 1) I want to have a custom JS package available for pyolite 2) I need to populate os.environ with a few variables from Camunda. (I could pinpoint those lines ones I have manged to update that branch.)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a custom JS package available for pyolite

right, so your kernel extension will have a worker.ts, which is what can actually touch pyodide. It is somewhat limiting, as it doesn't get webpacked, it can't really import anything, directly, and you'll need to get it the absolute URL of the library you're importing, and in kernel.ts, add another importScripts.

Then, inside worker.ts, after everything else gets set up, you'll have access to the ready pyodide and pyolite. On pidgy, we happen to only need to hack python stuff, but having JS works as well.

Note that some of the scoping is a little wonky: if you want somehting to be imported for real, you might have to put it into user_ns.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now I resolved my Camunda-needs by following pidgy / robotkernel approach for custom lite package and copy & pasted custom pyolite and robotkernels with required hacks.

import('@jupyterlite/server-extension')
];

Expand Down
2 changes: 2 additions & 0 deletions app/lab/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
"@jupyterlite/server-extension": "~0.1.0-beta.4",
"@jupyterlite/types": "~0.1.0-beta.4",
"@jupyterlite/ui-components": "~0.1.0-beta.4",
"@jupyterlite/robolite-kernel-extension": "~0.1.0-beta.4",
"@lumino/algorithm": "~1.9.1",
"@lumino/application": "~1.28.1",
"@lumino/commands": "~1.20.0",
Expand Down Expand Up @@ -145,6 +146,7 @@
"@jupyterlite/server-extension": "^0.1.0-beta.4",
"@jupyterlite/types": "^0.1.0-beta.4",
"@jupyterlite/ui-components": "^0.1.0-beta.4",
"@jupyterlite/robolite-kernel-extension": "^0.1.0-beta.3",
"es6-promise": "~4.2.8",
"react": "^17.0.1",
"react-dom": "^17.0.1",
Expand Down
1 change: 1 addition & 0 deletions app/retro/index.template.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ require('./style.js');
const serverExtensions = [
import('@jupyterlite/javascript-kernel-extension'),
import('@jupyterlite/pyolite-kernel-extension'),
import('@jupyterlite/robolite-kernel-extension'),
import('@jupyterlite/server-extension')
];

Expand Down
2 changes: 2 additions & 0 deletions app/retro/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"@jupyterlite/server": "~0.1.0-beta.4",
"@jupyterlite/server-extension": "~0.1.0-beta.4",
"@jupyterlite/types": "~0.1.0-beta.4",
"@jupyterlite/robolite-kernel-extension": "~0.1.0-beta.4",
"@lumino/algorithm": "~1.9.1",
"@lumino/application": "~1.28.1",
"@lumino/commands": "~1.20.0",
Expand Down Expand Up @@ -120,6 +121,7 @@
"@jupyterlite/server": "^0.1.0-beta.4",
"@jupyterlite/server-extension": "^0.1.0-beta.4",
"@jupyterlite/types": "^0.1.0-beta.4",
"@jupyterlite/robolite-kernel-extension": "^0.1.0-beta.4",
"@retrolab/application": "^0.3.20",
"@retrolab/application-extension": "^0.3.20",
"@retrolab/docmanager-extension": "^0.3.20",
Expand Down
8 changes: 7 additions & 1 deletion examples/jupyter_lite_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"https://github.com/conda-forge/releases/releases/download/noarch/jupyterlab_widgets-1.1.0-pyhd8ed1ab_0.tar.bz2/jupyterlab_widgets-1.1.0-pyhd8ed1ab_0.tar.bz2",
"https://github.com/conda-forge/releases/releases/download/noarch/plotly-5.6.0-pyhd8ed1ab_0.tar.bz2/plotly-5.6.0-pyhd8ed1ab_0.tar.bz2",
"https://github.com/conda-forge/releases/releases/download/noarch/theme-darcula-3.1.1-pyh3684270_0.tar.bz2/theme-darcula-3.1.1-pyh3684270_0.tar.bz2",
"https://github.com/jupyterlite/p5-kernel/releases/download/v0.1.0a12/jupyterlite_p5_kernel-0.1.0a12-py3-none-any.whl"
"https://github.com/jupyterlite/p5-kernel/releases/download/v0.1.0a12/jupyterlite_p5_kernel-0.1.0a12-py3-none-any.whl",
"https://files.pythonhosted.org/packages/py3/j/jupyterlab_robotmode/jupyterlab_robotmode-0.3.1-py3-none-any.whl"
],
"ignore_sys_prefix": true,
"output_dir": "../build/docs-app",
Expand All @@ -39,8 +40,10 @@
"https://files.pythonhosted.org/packages/py2.py3/i/ipyvue/ipyvue-1.7.0-py2.py3-none-any.whl",
"https://files.pythonhosted.org/packages/py2.py3/i/ipyvuetify/ipyvuetify-1.8.2-1-py2.py3-none-any.whl",
"https://files.pythonhosted.org/packages/py2.py3/i/ipywidgets/ipywidgets-7.7.0-py2.py3-none-any.whl",
"https://files.pythonhosted.org/packages/py2.py3/l/lunr/lunr-0.6.2-py2.py3-none-any.whl",
"https://files.pythonhosted.org/packages/py2.py3/m/mistune/mistune-0.8.4-py2.py3-none-any.whl",
"https://files.pythonhosted.org/packages/py2.py3/p/pandocfilters/pandocfilters-1.5.0-py2.py3-none-any.whl",
"https://files.pythonhosted.org/packages/42/ba/a9d64c7bcbc7e3e8e5f93a52721b377e994c22d16196e2b0f1236774353a/pathspec-0.9.0-py2.py3-none-any.whl",
"https://files.pythonhosted.org/packages/py2.py3/p/pexpect/pexpect-4.8.0-py2.py3-none-any.whl",
"https://files.pythonhosted.org/packages/py2.py3/p/pickleshare/pickleshare-0.7.5-py2.py3-none-any.whl",
"https://files.pythonhosted.org/packages/py2.py3/p/plotly/plotly-5.6.0-py2.py3-none-any.whl",
Expand All @@ -56,6 +59,7 @@
"https://files.pythonhosted.org/packages/py3/P/Pygments/Pygments-2.11.2-py3-none-any.whl",
"https://files.pythonhosted.org/packages/py3/S/Send2Trash/Send2Trash-1.8.0-py3-none-any.whl",
"https://files.pythonhosted.org/packages/py3/a/altair/altair-4.2.0-py3-none-any.whl",
"https://files.pythonhosted.org/packages/py3/b/black/black-22.1.0-py3-none-any.whl",
"https://files.pythonhosted.org/packages/py3/b/branca/branca-0.4.2-py3-none-any.whl",
"https://files.pythonhosted.org/packages/py3/c/charset-normalizer/charset_normalizer-2.0.12-py3-none-any.whl",
"https://files.pythonhosted.org/packages/py3/e/entrypoints/entrypoints-0.4-py3-none-any.whl",
Expand All @@ -74,6 +78,8 @@
"https://files.pythonhosted.org/packages/py3/p/prometheus-client/prometheus_client-0.13.1-py3-none-any.whl",
"https://files.pythonhosted.org/packages/py3/p/prompt-toolkit/prompt_toolkit-3.0.28-py3-none-any.whl",
"https://files.pythonhosted.org/packages/py3/p/pure-eval/pure_eval-0.2.2-py3-none-any.whl",
"https://files.pythonhosted.org/packages/py3/r/robotframework/robotframework-5.0-py3-none-any.whl",
"https://files.pythonhosted.org/packages/py3/r/robotkernel/robotkernel-1.6a2-py3-none-any.whl",
"https://files.pythonhosted.org/packages/py3/s/stack-data/stack_data-0.2.0-py3-none-any.whl",
"https://files.pythonhosted.org/packages/py3/t/tenacity/tenacity-8.0.1-py3-none-any.whl",
"https://files.pythonhosted.org/packages/py3/t/terminado/terminado-0.13.3-py3-none-any.whl",
Expand Down
217 changes: 217 additions & 0 deletions examples/robolite - smoketest.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
{
"metadata": {
"language_info": {
"codemirror_mode": {
"name": "robotframework",
"version": 3
},
"file_extension": ".robot",
"mimetype": "text/x-robotframework",
"name": "robotframework",
"nbconvert_exporter": "robotframework",
"pygments_lexer": "robotframework",
"version": "3.8"
},
"kernelspec": {
"name": "Robot Framework",
"display_name": "Robot Framework",
"language": "robotframework"
}
},
"nbformat_minor": 4,
"nbformat": 4,
"cells": [
{
"cell_type": "markdown",
"source": "## Robot Framework kernel for Jupyter Lite",
"metadata": {}
},
{
"cell_type": "markdown",
"source": "This notebook demonstrates the use of Robot Framework kernel for in-browser Jupyter Lite.",
"metadata": {}
},
{
"cell_type": "markdown",
"source": "At first we import `IPython.display` to have its display method as a keyword.",
"metadata": {}
},
{
"cell_type": "code",
"source": "*** Settings ***\n\nLibrary IPython.display",
"metadata": {
"trusted": true
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": "Then we try out successful task that produces log and report:",
"metadata": {}
},
{
"cell_type": "code",
"source": "*** Tasks ***\n\nThis task shall pass\n Should be equal 1 1",
"metadata": {
"tags": [],
"trusted": true
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": "So, happy path works. Let's see next how the failing task also shows us stderr from the execution:",
"metadata": {}
},
{
"cell_type": "code",
"source": "*** Tasks ***\n\nThis task shall fail\n Should be equal 1 2",
"metadata": {
"trusted": true
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": "It is also possible to define custom keywords. And make them require arguments.\n\nRobot Framework kernel injects widgets for the keywords to make it easier to try them out:",
"metadata": {}
},
{
"cell_type": "code",
"source": "*** Keywords ***\n\nThis keyword displays a word\n [Arguments] ${word}=Hello world\n Display ${word}",
"metadata": {
"trusted": true
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": "Unfortunately, due to [a bug](https://github.com/jupyterlite/jupyterlite/issues/303) in Jupyter Lite ipywidgets integration, the button output does not fully work: output may replace button or appear in wrong location.\n\nAlso, the failing keyword below does not show its stderr as it should:",
"metadata": {}
},
{
"cell_type": "code",
"source": "*** Keywords ***\n\nThis keyword should fail\n [Arguments] ${word}=Hello world\n Don't display ${word}",
"metadata": {
"trusted": true
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": "We expect these to be fixed pretty soon...",
"metadata": {}
},
{
"cell_type": "markdown",
"source": "Next, to the fancier features. Let's define inline Python module for custom Python keyword library:",
"metadata": {}
},
{
"cell_type": "code",
"source": "%%python module HelloWorld\n\nclass HelloWorld:\n def hello_world(self):\n return \"Hello World!\"",
"metadata": {
"trusted": true
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": "Now this module can be safely imported as a keyword library:",
"metadata": {}
},
{
"cell_type": "code",
"source": "*** Settings ***\n\nLibrary HelloWorld",
"metadata": {
"trusted": true
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": "And used in a task:",
"metadata": {}
},
{
"cell_type": "code",
"source": "*** Tasks ***\n\nTry out HelloWorld keywords\n Hello world",
"metadata": {
"trusted": true
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": "Adding existing 3rd party keyword libraries is more tricky, though.\n\nAt first, in-browser Jupyter Lite can only support pure Python packages (or packages pre-compiled to WASM). At second, Robot Framework kernel in this integration does not expose API to interact with the underlying Python environment.\n\nA workaround is to define a custom in-line module and import it twice by executing the cell. The first execution will trigger asynchronous package download. Once the package has been downloaded, execution should work.",
"metadata": {}
},
{
"cell_type": "code",
"source": "%%python module PackageManager\n\ntry:\n import OTP\nexcept ImportError:\n import micropip\n micropip.install([\"pyotp\", \"robotframework-otp\"])\n import OTP",
"metadata": {
"trusted": true
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": "*** Settings ***\n\nLibrary OTP",
"metadata": {
"trusted": true
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": "*** Tasks ***\n\nGet OTP from secret\n ${otp}= Get OTP base32secret3232",
"metadata": {
"trusted": true
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": "Finally, we learn to interact with the world from in-browser Jupyter Lite. Because the underlying Python implementation does not yet have `http.client`, we can only do request with JavaScript API, only using synchronous version of XMLHttpRequest, only for resources that support CORS.",
"metadata": {}
},
{
"cell_type": "code",
"source": "%%python module API\n\nfrom js import XMLHttpRequest\n\nimport json\n\n\nclass API:\n def get_data(self):\n url = f\"https://reqres.in/api/users\"\n request = XMLHttpRequest.new()\n request.open(\"GET\", url, False)\n request.send(None)\n assert request.status == 200\n return json.loads(request.responseText)",
"metadata": {
"trusted": true
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": "*** Settings ***\n\nLibrary API",
"metadata": {
"trusted": true
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": "*** Tasks ***\n\nGet some data\n ${data}= Get data",
"metadata": {
"trusted": true
},
"execution_count": null,
"outputs": []
}
]
}
18 changes: 18 additions & 0 deletions nix/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# https://github.com/nmattia/niv
{ sources ? import ./sources.nix
, nixpkgs ? sources."nixpkgs"
}:

let

overlay = _: pkgs: rec {
jupyterWith = pkgs.callPackage ./pkgs/jupyterWith {};
poetry2nix = pkgs.callPackage ./pkgs/poetry2nix { inherit nixpkgs; };
jupyterLiteEnv = pkgs.callPackage ./pkgs/jupyterLiteEnv{};
};

pkgs = import nixpkgs {
overlays = [ overlay ];
};

in pkgs
11 changes: 11 additions & 0 deletions nix/pkgs/jupyterLiteEnv/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{ poetry2nix }:

poetry2nix.mkPoetryEnv {
projectDir = ./.;
overrides = poetry2nix.overrides.withDefaults (self: super: {
jupyterlite = super.jupyterlite.overridePythonAttrs(old: {
src = ../../../py/jupyterlite;
nativeBuildInputs = [ self.flit-core ];
});
});
}
Loading