AGENTS.md#
A concise guide for AI coding agents working in this repository.
Project Overview#
Sparkmagic is a set of tools for interactively working with remote Spark clusters in Jupyter notebooks. Rather than running Spark locally, sparkmagic communicates with a remote Spark cluster through a REST server β currently supporting Livy, Lighter, and Ilum .
This is a three-package Python monorepo. All packages share the same version number (currently 0.23.0) and are released together .
The Three Packages#
| Package | Purpose |
|---|---|
hdijupyterutils | Foundational shared utilities: configuration, event handling, file I/O, IPython display helpers, logging |
autovizwidget | Automatic visualization of Pandas DataFrames using Plotly widgets |
sparkmagic | Core package: IPython magics (%%spark, %%sql, %%info), Jupyter kernels (PySpark, Scala, SparkR), Livy REST client, auth |
Key Features#
- Execute Spark code in Python, Scala, and R from a Jupyter notebook
- Automatic
SparkContext(sc) andHiveContext(sqlContext) creation %%sqlmagic with automatic query result visualization%%infofor Spark application info and logs- Capture SQL results as Pandas DataFrames for local analysis
- Send local files or DataFrames to a remote cluster
- Basic Access and Kerberos authentication to Livy
Repository Structure#
sparkmagic/ β repo root
βββ hdijupyterutils/ β Package 1: shared utilities
β βββ hdijupyterutils/ β Python source
β β βββ tests/
β β βββ configuration.py
β β βββ events.py / eventshandler.py
β β βββ filesystemreaderwriter.py
β β βββ ipythondisplay.py / ipywidgetfactory.py
β β βββ log.py / utils.py / guid.py / constants.py
β β βββ ...
β βββ requirements.txt
β βββ setup.py / setup.cfg
βββ autovizwidget/ β Package 2: auto-visualization
β βββ autovizwidget/
β β βββ tests/
β β βββ plotlygraphs/ β graphbase, linegraph, bargraph, areagraph, piegraph, scattergraph, graphrenderer
β β βββ widget/ β autovizwidget.py, encodingwidget.py, encoding.py
β β βββ utils/
β βββ requirements.txt
β βββ setup.py / setup.cfg
βββ sparkmagic/ β Package 3: main package
β βββ sparkmagic/
β β βββ tests/ β 26 test files
β β βββ auth/ β customauth.py, basic.py, kerberos.py
β β βββ controllerwidget/ β Jupyter UI widgets for cluster/session management
β β βββ kernels/ β pysparkkernel/, sparkkernel/, sparkrkernel/, wrapperkernel/
β β βββ livyclientlib/ β HTTP client, session mgmt, retry policies, command execution
β β βββ magics/ β remotesparkmagics.py, sparkmagicsbase.py
β β βββ serverextension/ β /reconnectsparkmagic endpoint
β β βββ utils/ β configuration.py, constants.py
β βββ example_config.json
β βββ requirements.txt
β βββ setup.py / setup.cfg
βββ examples/ β sample notebooks
βββ helm/ β Kubernetes Helm charts
βββ pyproject.toml β Poetry dev wrapper (NOT a publishable package)
βββ docker-compose.yml
βββ Dockerfile.jupyter
βββ Dockerfile.spark
βββ .github/workflows/tests.yml
Package Dependency Chain#
sparkmagic β autovizwidget β hdijupyterutils
β
sparkmagic β hdijupyterutils
hdijupyterutils has no internal dependencies. autovizwidget depends on hdijupyterutils. sparkmagic depends on both . Install in this order.
Root-Level pyproject.toml#
The pyproject.toml at the repo root is a Poetry convenience wrapper for development only β it declares all three local packages as path dependencies with develop = true . It is not the packaging configuration for any of the three distributed packages. Each package's own setup.py/setup.cfg governs what gets published to PyPI.
Development Setup#
Prerequisites#
-
Python 3.8β3.12
-
libkrb5-devon Ubuntu/Debian (required for Kerberos support βrequests-kerberoswill fail to build without it)sudo apt-get install -y libkrb5-dev
Option A: Direct Editable Install (Recommended for most contributors)#
Install the three packages in dependency order using pip's editable (-e) flag :
git clone https://github.com/jupyter-incubator/sparkmagic
cd sparkmagic
pip install -e hdijupyterutils
pip install -e autovizwidget
pip install -e sparkmagic
Order matters. Each package depends on the ones before it. Installing sparkmagic before hdijupyterutils will fail.
To also install test tools:
pip install pytest mock
Or install with requirements files (mirrors CI exactly):
pip install -r hdijupyterutils/requirements.txt -e hdijupyterutils
pip install -r autovizwidget/requirements.txt -e autovizwidget
pip install -r sparkmagic/requirements.txt -e sparkmagic
Option B: Poetry#
Use Poetry to create an isolated virtual environment with all three packages :
poetry install
If you encounter issues with numpy or pandas during poetry install:
poetry run pip install numpy pandas
poetry install
Run commands in the Poetry-managed environment:
poetry run pytest
poetry run jupyter notebook
Docker Development Environment#
Docker provides a self-contained stack: JupyterLab on port 8888 + a Livy server on port 8998 backed by a local-mode Spark instance .
docker compose build
docker compose up
# Access Jupyter at http://localhost:8888
# Use http://spark:8998 as the Livy endpoint inside notebooks
Dev mode β to test local code changes without pushing to PyPI, edit docker-compose.yml and set :
args:
dev_mode: "true"
Then rebuild:
docker compose build
docker compose up
With dev_mode: "true", the container installs all three packages in editable mode (-e) from the copied source, enabling real-time debugging inside JupyterLab .
To stop:
# Ctrl-C to interrupt, then optionally:
docker compose down
One-Time Runtime Config#
Sparkmagic looks for its config at ~/.sparkmagic/config.json. This directory is not created automatically by the install. Create it manually before running tests or starting Jupyter:
mkdir -p ~/.sparkmagic
# Optionally copy the example config:
cp sparkmagic/example_config.json ~/.sparkmagic/config.json
Key Commands#
Install#
# Editable install of all three packages (in dependency order)
pip install -e hdijupyterutils
pip install -e autovizwidget
pip install -e sparkmagic
# With explicit requirements (mirrors CI)
pip install -r hdijupyterutils/requirements.txt -e hdijupyterutils
pip install -r autovizwidget/requirements.txt -e autovizwidget
pip install -r sparkmagic/requirements.txt -e sparkmagic
# Install test dependencies
pip install pytest mock
# Poetry (all-in-one)
poetry install
Testing#
# Run each package's tests separately (preferred β mirrors CI)
pytest hdijupyterutils
pytest autovizwidget
mkdir -p ~/.sparkmagic # required before sparkmagic tests
pytest sparkmagic
# With Poetry
poetry run pytest hdijupyterutils
poetry run pytest autovizwidget
poetry run pytest sparkmagic
Note: Pass the package directory name to
pytest, not the inner source directory.pytest sparkmagicis correct;pytest sparkmagic/sparkmagic/tests/also works but is less idiomatic.
Docker#
# Build images
docker compose build
# Start stack (Jupyter :8888, Livy :8998)
docker compose up
# Stop and remove containers
docker compose down
Kernel & Extension Registration#
# Find the sparkmagic install path
pip show sparkmagic
# Install kernels (run from the sparkmagic install location)
jupyter-kernelspec install sparkmagic/kernels/sparkkernel
jupyter-kernelspec install sparkmagic/kernels/pysparkkernel
jupyter-kernelspec install sparkmagic/kernels/sparkrkernel
# Enable server extension
jupyter server extension enable --py sparkmagic
Release (maintainers only)#
Releases are fully automated via GitHub Actions . Go to Actions β Release β Run workflow, select the bump type (patch / minor / major), and confirm. The workflow bumps all three package versions together using .bumpversion.cfg.
Testing#
Overview#
Each of the three packages has its own test suite. Tests must be run per package by passing the package directory name to pytest. There is no single root-level pytest configuration β test discovery is fully automatic .
The Key Commands section above covers the exact pytest invocations. This section explains the test infrastructure in more detail.
Running Tests#
# 1. Prepare the sparkmagic config dir (required for sparkmagic tests only)
mkdir -p ~/.sparkmagic
# 2. Run each package's tests
pytest hdijupyterutils # 6 test files
pytest autovizwidget # 7 test files
pytest sparkmagic # 26 test files
Test Layout#
Tests live inside the package's source directory, not at the package root:
hdijupyterutils/hdijupyterutils/tests/ β 6 test files
autovizwidget/autovizwidget/tests/ β 7 test files
sparkmagic/sparkmagic/tests/ β 26 test files (covers magics, livyclientlib, kernels, auth, etc.)
Each tests/ directory contains an empty __init__.py .
Test Infrastructure#
- Framework:
pytest+mocklibrary only. Noconftest.py,pytest.ini,tox.ini, or custom plugins . - Fixtures: Tests use standard
setup_function()/teardown_function()pytest conventions for per-test setup. - Mocking: The
mocklibrary is used extensively for isolating Livy HTTP calls and IPython display interactions.
CI Matrix#
GitHub Actions runs tests against Python 3.8, 3.9, 3.10, 3.11, and 3.12 on every push and pull request . CI installs libkrb5-dev before running tests to support Kerberos-related packages.
System Dependency#
On Ubuntu/Debian, Kerberos headers are required:
sudo apt-get install -y libkrb5-dev
Without this, pip install -r sparkmagic/requirements.txt will fail because requests-kerberos requires native Kerberos headers to build.
Architecture & Conventions#
High-Level Architecture#
Sparkmagic executes all Spark code remotely via a Livy REST API β no Spark components are required on the Jupyter server . The flow is:
Jupyter Notebook / Magic
β (submit code/SQL as JSON)
livyclientlib (REST client)
β (HTTP to :8998)
Livy / Lighter / Ilum (REST server)
β
Remote Spark Cluster
Results are returned as plain text or JSON, deserialized by sparkmagic, and rendered as formatted text or Pandas DataFrames.
Module Organization#
Each package follows a consistent internal layout: <package_name>/<package_name>/ contains the source, with tests/ as a subdirectory:
<package>/
<package>/ β importable module
tests/ β test files
*.py β implementation
setup.py
requirements.txt
setup.cfg
Key Modules in sparkmagic#
| Module | Role |
|---|---|
magics/remotesparkmagics.py | IPython %%spark, %%sql, %%info, %%local magic implementations |
magics/sparkmagicsbase.py | Base class for all magics |
livyclientlib/livysession.py | Manages a single Livy interactive session lifecycle |
livyclientlib/reliablehttpclient.py | HTTP client with retry logic |
livyclientlib/configurableretrypolicy.py | Configurable retry/backoff policy |
livyclientlib/sqlquery.py | SQL query execution against Livy |
kernels/wrapperkernel/ | Base Jupyter kernel implementation (wraps a Livy session) |
auth/customauth.py | Pluggable Authenticator base class |
utils/configuration.py | Central runtime configuration (reads ~/.sparkmagic/config.json) |
serverextension/ | /reconnectsparkmagic Jupyter server extension |
Authentication Pattern#
Authentication is pluggable . The Authenticator base class in auth/customauth.py is a requests-compatible auth object. Subclass it and override __call__(request) to attach credentials. Built-in implementations: basic.py and kerberos.py. Custom authenticators are registered in ~/.sparkmagic/config.json under the "authenticators" key.
Kernel Pattern#
Three language-specific kernels (pysparkkernel, sparkkernel, sparkrkernel) are thin wrappers around a common wrapperkernel base . Each kernel directory includes a kernel.json for Jupyter kernel registration. The kernels communicate with Livy using the livyclientlib session management layer.
Configuration#
Runtime configuration is read from ~/.sparkmagic/config.json. The sparkmagic/utils/configuration.py module provides typed accessors for all config keys. Configuration can also be overridden programmatically in a notebook:
import sparkmagic.utils.configuration as conf
conf.override('cleanup_all_sessions_on_exit', True)
Versioning & Code Style#
- All three packages share one semantic version, bumped together via
.bumpversion.cfg. - Code style: Black (enforced, see README badge).
pandas < 3.0.0is pinned across all packages .
Gotchas & Important Notes#
1. Install Order Is Non-Negotiable#
Always install the three packages in dependency order: hdijupyterutils β autovizwidget β sparkmagic. Installing sparkmagic first will fail because it imports from the other two at install time .
# β
Correct
pip install -e hdijupyterutils
pip install -e autovizwidget
pip install -e sparkmagic
# β Will fail
pip install -e sparkmagic # hdijupyterutils and autovizwidget not installed yet
2. ~/.sparkmagic Must Exist Before Running sparkmagic Tests#
The sparkmagic test suite reads from ~/.sparkmagic/ at import time. If the directory does not exist, tests will error out immediately. This is a required manual step β nothing creates this directory automatically :
mkdir -p ~/.sparkmagic
pytest sparkmagic
3. Pass Package Directory Names to pytest, Not Inner Test Paths#
The official pattern is pytest <package_dir>, not pytest <package_dir>/<package_dir>/tests/. Both work, but the directory-level invocation correctly mirrors CI and picks up all tests via pytest's automatic discovery :
pytest hdijupyterutils # β
matches CI
pytest autovizwidget # β
matches CI
pytest sparkmagic # β
matches CI
4. Docker Dev Mode Still Requires a Rebuild for New Files#
Setting dev_mode: "true" installs packages with -e inside the container, so edits to already-copied files are reflected live. However, the COPY instruction in Dockerfile.jupyter snapshots the local source at build time . If you add new files or make structural changes, you must re-run docker compose build before docker compose up.
5. requests-kerberos Needs Native Kerberos Headers on Linux#
pip install requests-kerberos fails on Debian/Ubuntu without libkrb5-dev. Docker works around this by using conda install requests-kerberos -y instead of pip . For local development on Linux, install the system package first:
sudo apt-get install -y libkrb5-dev
6. Root pyproject.toml Is Not a Publishable Package#
The pyproject.toml at the repo root is a Poetry development environment wrapper named "development" . It is not the build configuration for any of the three packages. Each package is independently published to PyPI from its own setup.py. Do not modify the root pyproject.toml expecting it to affect package metadata on PyPI.
7. All Packages Are Versioned Together#
Version bumps must update all three packages simultaneously. Use .bumpversion.cfg or the GitHub Actions release workflow. Never manually edit __version__ in just one package β they must stay in sync .
8. Docker Livy Endpoint Is http://spark:8998, Not localhost#
Inside the Docker stack, the Jupyter service resolves the Livy server by its Docker Compose service hostname spark. When configuring a sparkmagic endpoint from inside the JupyterLab container, use http://spark:8998, not http://localhost:8998 .
9. conf.override for Session Cleanup Must Be Called Before %load_ext#
If you set cleanup_all_sessions_on_exit via conf.override(...), this must happen before loading the sparkmagic extension in a notebook. Calling it after %load_ext sparkmagic.magics has no effect .