diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..5abd31e --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,57 @@ +name: Docs + +on: + push: + branches: [main] + pull_request: + types: [opened, synchronize, reopened] + +permissions: + contents: write + issues: write + pull-requests: write + +concurrency: + group: docs-pages + cancel-in-progress: true + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install documentation dependencies + run: | + python -m pip install --upgrade pip + pip install -e .[docs] + + - name: Build + run: mkdocs build --clean + + - name: Publish to gh-pages + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_branch: gh-pages + publish_dir: ./site + destination_dir: ${{ github.event_name == 'push' && '' || format('pr-{0}', github.event.pull_request.number) }} + keep_files: true + + - name: Comment preview URL on PR + if: github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + const pr = context.payload.pull_request.number; + const url = `https://${context.repo.owner}.github.io/${context.repo.repo}/pr-${pr}/`; + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pr, + body: `Docs preview: ${url}` + }); diff --git a/.gitignore b/.gitignore index a97e19e..e9eeb71 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,6 @@ __pycache__/ dist/ build/ *.egg + +# Documentation +site/ diff --git a/docs/api/index.md b/docs/api/index.md new file mode 100644 index 0000000..65d76a2 --- /dev/null +++ b/docs/api/index.md @@ -0,0 +1,30 @@ +--- +title: API Overview +--- + +# API Overview + +The Jumper Wrapper Kernel exposes a modular API organized into three main components: + +- **Kernel** - The main `JumperWrapperKernel` class that implements the Jupyter kernel protocol and manages kernel wrapping +- **Installation** - Functions for installing and uninstalling the kernel specification +- **Utilities** - Helper functions for magic command detection and cell routing + +## Architecture +To be described + +## Message Flow + +1. Cell code arrives at `do_execute()` +2. `_is_local_magic()` checks if code contains only local magic commands +3. Local magics are executed via `_execute_local_magic()` +4. Other code is forwarded via `_forward_to_wrapped_kernel()` +5. Pre/post cell events are triggered for jumper-extension hooks + +## Module Reference + +For detailed API documentation, see: + +- [Kernel](kernel.md) - Main kernel class and magic commands +- [Installation](install.md) - Kernel installation functions +- [Utilities](utilities.md) - Magic detection helpers diff --git a/docs/api/install.md b/docs/api/install.md new file mode 100644 index 0000000..4b54e22 --- /dev/null +++ b/docs/api/install.md @@ -0,0 +1,17 @@ +--- +title: Installation +--- + +# Installation Module + +The installation module provides functions for installing and uninstalling the Jumper Wrapper Kernel specification. + +## Functions + +::: jumper_wrapper_kernel.install + options: + show_root_heading: false + members: + - install_kernel + - uninstall_kernel + - main diff --git a/docs/api/kernel.md b/docs/api/kernel.md new file mode 100644 index 0000000..f4c221d --- /dev/null +++ b/docs/api/kernel.md @@ -0,0 +1,28 @@ +--- +title: Kernel +--- + +# Kernel Module + +The kernel module contains the main `JumperWrapperKernel` class and the `JumperWrapperMagics` class for magic command handling. + +## JumperWrapperMagics + +::: jumper_wrapper_kernel.kernel.JumperWrapperMagics + options: + show_root_heading: true + members: + - list_kernels + - wrap_kernel + +## JumperWrapperKernel + +::: jumper_wrapper_kernel.kernel.JumperWrapperKernel + options: + show_root_heading: true + members: + - do_execute + - do_shutdown + - do_complete + - do_inspect + - kernel_info diff --git a/docs/api/utilities.md b/docs/api/utilities.md new file mode 100644 index 0000000..c228577 --- /dev/null +++ b/docs/api/utilities.md @@ -0,0 +1,18 @@ +--- +title: Utilities +--- + +# Utilities Module + +The utilities module provides helper functions for detecting and classifying magic commands in cell content. + +## Functions + +::: jumper_wrapper_kernel.utilities + options: + show_root_heading: false + members: + - get_line_magics_cached + - is_known_line_magic + - is_pure_line_magic_cell + - is_local_magic_cell diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md new file mode 100644 index 0000000..942355a --- /dev/null +++ b/docs/getting-started/installation.md @@ -0,0 +1,64 @@ +--- +title: Installation +--- + +# Installation + +## Requirements + +- Python >= 3.8 +- ipykernel >= 6.0 +- jupyter_client >= 7.0 +- jumper-extension >= 0.3.0 + +## Standard Installation + +Install the package from PyPI: + +```bash +pip install jumper_wrapper_kernel +``` + +Then install the kernel specification: + +```bash +python -m jumper_wrapper_kernel.install install +``` + +## Installation for Virtual Environments + +If you're using a virtual environment or conda, install to `sys.prefix`: + +```bash +python -m jumper_wrapper_kernel.install install --sys-prefix +``` + +## Development Installation + +Clone the repository and install in editable mode: + +```bash +git clone https://github.com/ScaDS/jumper_wrapper_kernel.git +cd jumper_wrapper_kernel +pip install -e . +python -m jumper_wrapper_kernel.install install +``` + +## Verification + +After installation, verify the kernel is available: + +```bash +jupyter kernelspec list +``` + +You should see `jumper_wrapper` in the list of available kernels. + +## Uninstallation + +To remove the kernel: + +```bash +python -m jumper_wrapper_kernel.install uninstall +pip uninstall jumper_wrapper_kernel +``` diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md new file mode 100644 index 0000000..d5c55e1 --- /dev/null +++ b/docs/getting-started/quickstart.md @@ -0,0 +1,72 @@ +--- +title: Quickstart +--- + +# Quickstart + +This guide shows you how to use the Jumper Wrapper Kernel in a few simple steps. + +## Step 1: Select the Kernel + +1. Start Jupyter Notebook or JupyterLab +2. Create a new notebook +3. Select **Jumper Wrapper Kernel** as your kernel + +## Step 2: List Available Kernels + +See which kernels can be wrapped: + +```python +%list_kernels +``` + +Output: +``` +Available Jupyter Kernels: +-------------------------------------------------- + python3: Python 3 (ipykernel) (python) + ir: R (r) + julia-1.9: Julia 1.9 (julia) +-------------------------------------------------- +``` + +## Step 3: Wrap a Kernel + +Wrap your desired kernel: + +```python +%wrap_kernel python3 +``` + +Output: +``` +Successfully wrapped kernel: python3 +Hint: Refresh the page (without restarting the kernel) to enable syntax highlighting for the wrapped language. +``` + +## Step 4: Use Performance Monitoring + +Now you can use jumper-extension commands while running code on the wrapped kernel: + +```python +# Start monitoring (handled locally) +%perfmonitor_start + +# Run code on the wrapped kernel +import numpy as np +x = np.random.rand(1000, 1000) +y = np.dot(x, x.T) + +# View performance report (handled locally) +%perfmonitor_perfreport +``` + +## How It Works + +The Jumper Wrapper Kernel acts as a proxy: + +1. **Magic commands** from jumper-extension are intercepted and executed locally +2. **All other code** is forwarded to the wrapped kernel +3. **Output** is streamed back to the notebook + +This allows you to monitor performance of any Jupyter kernel, regardless of its language. diff --git a/docs/guides/magic-commands.md b/docs/guides/magic-commands.md new file mode 100644 index 0000000..649b38a --- /dev/null +++ b/docs/guides/magic-commands.md @@ -0,0 +1,69 @@ +--- +title: Magic Commands +--- + +# Magic Commands + +The Jumper Wrapper Kernel provides its own magic commands for kernel management, plus full access to jumper-extension magic commands for performance monitoring. + +## Wrapper Magic Commands + +### `%list_kernels` + +Lists all available Jupyter kernels that can be wrapped. + +```python +%list_kernels +``` + +Output: +``` +Available Jupyter Kernels: +-------------------------------------------------- + python3: Python 3 (ipykernel) (python) + ir: R (r) + julia-1.9: Julia 1.9 (julia) +-------------------------------------------------- +Currently wrapped kernel: python3 +``` + +### `%wrap_kernel` + +Wraps an existing Jupyter kernel. All subsequent code (except local magic commands) will be forwarded to this kernel. + +**Basic usage:** + +```python +%wrap_kernel python3 +``` + +**With permanent kernel spec:** + +```python +%wrap_kernel ir --save jumper-r +``` + +This creates a new kernel spec `jumper-r` that automatically wraps the R kernel on startup. + +## Jumper Extension Commands + +All jumper-extension magic commands are available and executed locally: + +| Command | Description | +|---------|-------------| +| `%perfmonitor_start [interval]` | Start performance monitoring | +| `%perfmonitor_stop` | Stop performance monitoring | +| `%perfmonitor_perfreport` | View performance report | +| `%perfmonitor_plot` | Plot performance data | +| `%cell_history` | View cell execution history | + +For complete documentation on jumper-extension commands, see the [jumper-extension documentation](https://scads.github.io/jumper_ipython_extension/). + +## Command Routing + +The kernel automatically routes commands: + +- **Local execution**: Wrapper magics and jumper-extension magics +- **Forwarded to wrapped kernel**: All other code + +This routing is determined by analyzing the cell content before execution. diff --git a/docs/guides/wrap-kernel.md b/docs/guides/wrap-kernel.md new file mode 100644 index 0000000..70f5d38 --- /dev/null +++ b/docs/guides/wrap-kernel.md @@ -0,0 +1,23 @@ +# How to Wrap a new Kernel + +=== "Presentation" + + +=== "Video" + \ No newline at end of file diff --git a/docs/img/JUmPER01.png b/docs/img/JUmPER01.png new file mode 100644 index 0000000..5a98ca8 Binary files /dev/null and b/docs/img/JUmPER01.png differ diff --git a/docs/img/JUmPER01.svg b/docs/img/JUmPER01.svg new file mode 100644 index 0000000..63c61df --- /dev/null +++ b/docs/img/JUmPER01.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..cc523c0 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,20 @@ +--- +title: Jumper Wrapper Kernel Documentation +--- + +# Jumper Wrapper Kernel + +

+ JUmPER logo +

+ +Welcome to the documentation for the Jumper Wrapper Kernel, a Jupyter kernel that wraps other kernels while providing jumper-extension performance monitoring capabilities. + +Jumper Wrapper Kernel lets you: + +- **Wrap any Jupyter kernel** (Python, R, Julia, etc.) with a single magic command +- **Monitor performance** of any wrapped kernel using jumper-extension magic commands +- **Forward code transparently** to the wrapped kernel while keeping monitoring local +- **Create permanent kernel specs** that auto-wrap specific kernels on startup + +To get started quickly, follow the steps in the [Installation](getting-started/installation.md) and [Quickstart](getting-started/quickstart.md) guides. For detailed command descriptions and programmatic usage, refer to the [API Reference](api/index.md) section. diff --git a/jumper_wrapper_kernel/install.py b/jumper_wrapper_kernel/install.py index 53623d7..802ca93 100644 --- a/jumper_wrapper_kernel/install.py +++ b/jumper_wrapper_kernel/install.py @@ -20,7 +20,12 @@ def install_kernel(user=True, prefix=None): - """Install the kernel spec.""" + """Install the Jumper Wrapper Kernel specification. + + Args: + user: If True, install for current user only. Ignored if prefix is set. + prefix: Install to specific prefix path (e.g., sys.prefix for virtualenv). + """ import tempfile import shutil @@ -52,7 +57,7 @@ def install_kernel(user=True, prefix=None): def uninstall_kernel(): - """Uninstall the kernel spec.""" + """Remove the Jumper Wrapper Kernel specification.""" kernel_spec_manager = KernelSpecManager() try: @@ -64,6 +69,7 @@ def uninstall_kernel(): def main(): + """CLI entry point for kernel installation and removal.""" parser = argparse.ArgumentParser(description='Install/Uninstall Jumper Wrapper Kernel') parser.add_argument('action', choices=['install', 'uninstall'], help='Action to perform') parser.add_argument('--user', action='store_true', default=True, diff --git a/jumper_wrapper_kernel/kernel.py b/jumper_wrapper_kernel/kernel.py index d1e0528..c56a6de 100644 --- a/jumper_wrapper_kernel/kernel.py +++ b/jumper_wrapper_kernel/kernel.py @@ -32,6 +32,12 @@ class JumperWrapperMagics(Magics): """Magic commands for the Jumper Wrapper Kernel.""" def __init__(self, shell, kernel): + """Initialize magic commands with shell and kernel references. + + Args: + shell: IPython shell instance. + kernel: JumperWrapperKernel instance for delegating operations. + """ super().__init__(shell) self._kernel = kernel @@ -78,6 +84,11 @@ class JumperWrapperKernel(IPythonKernel): help="Kernel name to automatically wrap on startup. If empty, no auto-wrap.") def __init__(self, **kwargs): + """Initialize the wrapper kernel. + + Loads jumper-extension, registers wrapper magics, and prepares + for optional auto-wrapping based on configuration. + """ super().__init__(**kwargs) # Wrapped kernel state diff --git a/jumper_wrapper_kernel/utilities.py b/jumper_wrapper_kernel/utilities.py index f065e0b..2c96635 100644 --- a/jumper_wrapper_kernel/utilities.py +++ b/jumper_wrapper_kernel/utilities.py @@ -8,11 +8,25 @@ @lru_cache(maxsize=1) def get_line_magics_cached() -> FrozenSet[str]: + """Return cached set of all registered line magic names. + + Returns: + Frozen set of magic command names (without % prefix). + """ ip = get_ipython() return frozenset(ip.magics_manager.lsmagic().get("line", [])) def is_known_line_magic(line: str, line_magics: frozenset) -> bool: + """Check if a line starts with a known magic command. + + Args: + line: Single line of code to check. + line_magics: Set of known magic names (without % prefix). + + Returns: + True if line starts with %, False otherwise. + """ s = line.lstrip() if not s.startswith("%"): return False diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..a8f85be --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,33 @@ +site_name: Jumper Wrapper Kernel +site_description: A Jupyter kernel that wraps other kernels with jumper-extension support +repo_url: https://github.com/ScaDS/jumper_wrapper_kernel +repo_name: ScaDS/jumper_wrapper_kernel + +docs_dir: docs + +theme: + name: material + logo: img/JUmPER01.svg + +nav: + - Home: index.md + - Getting started: + - Installation: getting-started/installation.md + - Quickstart: getting-started/quickstart.md + - User Guide: + - Magic Commands: guides/magic-commands.md + - Auto-Wrap Kernels: guides/wrap-kernel.md + - API Reference: + - Overview: api/index.md + - Kernel: api/kernel.md + - Installation: api/install.md + - Utilities: api/utilities.md + +markdown_extensions: + - pymdownx.superfences + - pymdownx.tabbed: + alternate_style: true + +plugins: + - search + - mkdocstrings diff --git a/pyproject.toml b/pyproject.toml index 22e6f76..dc6ddda 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,6 +38,11 @@ dev = [ "pytest-cov", ] +docs = [ + "mkdocs-material", + "mkdocstrings[python]", +] + [project.urls] Homepage = "https://github.com/ScaDS/jumper_wrapper_kernel" Repository = "https://github.com/ScaDS/jumper_wrapper_kernel"