AGENTS.md#
A concise reference guide for AI coding agents (and human contributors) working in the sparkmagic repository.
Project Overview#
Sparkmagic is a set of tools for interactively working with remote Spark clusters inside Jupyter notebooks. It communicates with Spark through a REST server β primarily Apache Livy, but also compatible with Lighter and Ilum β and supports Python (PySpark), Scala, and R execution.
Three-Package Architecture#
The repository is a monorepo containing three independently distributed Python packages with a layered dependency graph :
hdijupyterutils β base layer (no internal deps)
β
autovizwidget β depends on hdijupyterutils
β
sparkmagic β depends on both
| Package | PyPI Name | Purpose |
|---|---|---|
hdijupyterutils/ | hdijupyterutils | Shared utilities: configuration, event handling, file I/O, logging, IPython display helpers, widget factory |
autovizwidget/ | autovizwidget | Auto-visualization widget for pandas DataFrames using Plotly; displays results of SQL queries automatically |
sparkmagic/ | sparkmagic | Main package: Livy client, Jupyter kernels (PySpark/Scala/R), IPython magics, auth handlers, server extension |
Two Usage Modes#
- IPython magic: Load
sparkmagicin a standard Python kernel and use%%spark,%%sql,%%scala,%%sparkrcell magics. - Dedicated kernels: Use the PySpark, Spark (Scala), or SparkR kernels directly β every cell is executed on the remote cluster by default.
Current version: 0.23.0 β requires Python 3.8+
Development Setup#
Prerequisites#
-
Python 3.8+
-
Kerberos headers (required to build
requests-kerberos):# Debian / Ubuntu sudo apt-get install -y libkrb5-dev # macOS (via Homebrew) brew install krb5 # Conda (avoids the header requirement entirely) conda install requests-kerberos -y
Option A β Editable pip installs (recommended for most development)#
Install the packages in dependency order β bottom-up. Order matters because autovizwidget and sparkmagic list the other packages as install requirements.
git clone https://github.com/jupyter-incubator/sparkmagic
cd sparkmagic
pip install -r hdijupyterutils/requirements.txt -e hdijupyterutils
pip install -r autovizwidget/requirements.txt -e autovizwidget
pip install -r sparkmagic/requirements.txt -e sparkmagic
Each pip install -e <pkg> installs the package in editable mode so changes to source files take effect immediately without reinstalling.
Option B β Poetry (all packages in one virtual environment)#
The root pyproject.toml defines a single Poetry project named development that pulls in all three packages as editable path dependencies.
poetry install # creates venv and installs all packages + dev deps
poetry run pytest # run full test suite
poetry run python # drop into the venv's Python
Note: The root
pyproject.tomlis for development only β it is not the source of truth for PyPI distribution. Packages are distributed via the individualsetup.pyfiles inside each sub-directory.
Configuration file for kernels#
The Spark kernels look for ~/.sparkmagic/config.json at startup. Copy the example and adjust the Livy URL:
mkdir -p ~/.sparkmagic
cp sparkmagic/example_config.json ~/.sparkmagic/config.json
# Edit ~/.sparkmagic/config.json β set "url" to your Livy endpoint
The default URL in the example config is http://localhost:8998.
Gotcha: The root
config.jsonfile in the repository is empty β it is a placeholder. Always usesparkmagic/example_config.jsonas your reference.
Docker development environment#
The repo ships two Dockerfiles and a docker-compose.yml that stands up a complete local Spark + Jupyter stack.
| Container | Image | Port | Purpose |
|---|---|---|---|
spark | Dockerfile.spark | 8998 | Apache Spark + Livy REST server |
jupyter | Dockerfile.jupyter | 8888 | Jupyter notebook with sparkmagic installed |
# Build and start both containers
docker-compose build
docker-compose up
# Open http://localhost:8888 in your browser
Using local source in Docker (dev mode)#
By default the Jupyter container installs sparkmagic from PyPI. To use your local source code instead, enable dev mode:
# docker-compose.yml β change the jupyter build args:
build:
context: .
dockerfile: Dockerfile.jupyter
args:
dev_mode: "true" # <-- change from "false"
When dev_mode=true, the Dockerfile runs pip install -e . for all three packages instead of pip install sparkmagic.
Gotcha:
docker-compose.ymlships withdev_mode: "false". You must explicitly change this argument β it is easy to forget and run tests against the published PyPI version instead of your local changes.
Key Commands#
Running Tests#
Tests are run per-package. The sparkmagic package requires ~/.sparkmagic/ to exist before tests run.
# hdijupyterutils (6 test files)
pytest hdijupyterutils
# autovizwidget (7 test files)
pytest autovizwidget
# sparkmagic (26 test files) β must create the config dir first
mkdir -p ~/.sparkmagic
pytest sparkmagic
With Poetry:
poetry run pytest hdijupyterutils
poetry run pytest autovizwidget
mkdir -p ~/.sparkmagic && poetry run pytest sparkmagic
Run a specific test file or test:
pytest sparkmagic/sparkmagic/tests/test_livysession.py
pytest sparkmagic/sparkmagic/tests/test_livysession.py::test_start_success
Installing / Building Packages#
# Editable installs (local development)
pip install -r hdijupyterutils/requirements.txt -e hdijupyterutils
pip install -r autovizwidget/requirements.txt -e autovizwidget
pip install -r sparkmagic/requirements.txt -e sparkmagic
# Production install from PyPI
pip install sparkmagic
# Build distribution archives (run inside each package directory)
cd hdijupyterutils && python -m build && cd ..
cd autovizwidget && python -m build && cd ..
cd sparkmagic && python -m build && cd ..
Registering Jupyter Kernels#
After installing sparkmagic, kernel specs must be registered separately:
SPARKMAGIC_LOC=$(pip show sparkmagic | grep Location | cut -d" " -f2)
jupyter-kernelspec install --user $SPARKMAGIC_LOC/sparkmagic/kernels/pysparkkernel
jupyter-kernelspec install --user $SPARKMAGIC_LOC/sparkmagic/kernels/sparkkernel
jupyter-kernelspec install --user $SPARKMAGIC_LOC/sparkmagic/kernels/sparkrkernel
# Enable the server extension (for the widget/config UI)
jupyter server extension enable --py sparkmagic
Linting & Formatting#
# Format all Python files with Black (required before every commit)
black .
# Check formatting without modifying files (what CI does)
black --check .
Black is the only code style tool enforced by CI.
Docker Commands#
# Build images
docker-compose build
# Start full stack (Spark + Jupyter)
docker-compose up
# Start in background
docker-compose up -d
# View logs
docker-compose logs -f jupyter
# Stop and remove containers
docker-compose down
Architecture & Structure#
Repository Layout#
sparkmagic/ β repo root
βββ hdijupyterutils/ β package 1 (base utilities)
β βββ hdijupyterutils/ β Python package
β β βββ tests/
β β βββ configuration.py
β β βββ events.py
β β βββ filehandler.py
β β βββ filesystemreaderwriter.py
β β βββ ipythondisplay.py
β β βββ ipywidgetfactory.py
β β βββ log.py
β β βββ utils.py
β βββ requirements.txt
β βββ setup.py
βββ autovizwidget/ β package 2 (visualization)
β βββ autovizwidget/
β β βββ tests/
β β βββ plotlygraphs/ β area, bar, line, pie, scatter graphs
β β βββ widget/ β AutoVizWidget, EncodingWidget
β β βββ utils/
β βββ requirements.txt
β βββ setup.py
βββ sparkmagic/ β package 3 (main package)
β βββ sparkmagic/
β β βββ tests/
β β βββ auth/ β Basic, Kerberos, Custom auth
β β βββ controllerwidget/ β cluster management UI
β β βββ kernels/ β pysparkkernel, sparkkernel, sparkrkernel
β β β βββ wrapperkernel/ β SparkKernelBase
β β βββ livyclientlib/ β core Livy client layer
β β βββ magics/ β IPython magic commands
β β βββ serverextension/ β Jupyter server extension handlers
β β βββ utils/ β configuration, constants, logging
β βββ example_config.json
β βββ requirements.txt
β βββ setup.py
βββ examples/ β example Jupyter notebooks
βββ helm/ β Kubernetes Helm chart
βββ .github/workflows/ β CI/CD pipelines
βββ Dockerfile.jupyter
βββ Dockerfile.spark
βββ docker-compose.yml
βββ pyproject.toml β Poetry dev environment (not for distribution)
βββ .bumpversion.cfg β synchronized version management
Key Modules#
sparkmagic/livyclientlib/ β Core Livy Client #
| Module | Purpose |
|---|---|
livysession.py | Manages a single Livy session lifecycle (start, execute, delete) |
sparkcontroller.py | Coordinates sessions and routes magic commands |
reliablehttpclient.py | HTTP client with retry logic |
livyreliablehttpclient.py | Livy-specific HTTP wrapper |
endpoint.py | Represents a Livy endpoint (URL + auth) |
sqlquery.py | SQL query abstraction |
configurableretrypolicy.py / linearretrypolicy.py | Retry strategies |
sparkmagic/magics/ #
remotesparkmagics.pyβ Defines the%%spark,%%sql,%%scala,%%sparkrcell magics and%sparkline magic.sparkmagicsbase.pyβ Base class shared by magic and kernel implementations.
sparkmagic/kernels/ #
pysparkkernel/pysparkkernel.pyβ PySpark Jupyter kernelsparkkernel/sparkkernel.pyβ Scala Spark Jupyter kernelsparkrkernel/sparkrkernel.pyβ SparkR Jupyter kernelwrapperkernel/sparkkernelbase.pyβ Shared base class for all three kernels
Each kernel directory also contains kernel.json (Jupyter kernel spec) and kernel.js.
sparkmagic/auth/ #
basic.pyβ HTTP Basic authenticationkerberos.pyβ Kerberos/SPNEGO authenticationcustomauth.pyβ Base class for custom auth implementations
Custom auth can be registered via authenticators in config.json.
sparkmagic/utils/configuration.py#
Global configuration singleton loaded from ~/.sparkmagic/config.json. Overriding is done in tests via conf.override_all({}).
Data Flow#
Jupyter cell input
β
Magic / Kernel (sparkmagicsbase.py)
β
SparkController (sparkcontroller.py)
β
LivyReliableHTTPClient (HTTP + retry)
β
Apache Livy REST API (port 8998)
β
Remote Spark Cluster
β
Response β DataFrameParser β AutoVizWidget β Cell output
Conventions#
Code Style#
-
Black is the sole enforced formatter. All code must be formatted before opening a PR β the
lint.yamlCI workflow runsblack --checkon every push and PR and will fail if any file is unformatted.black . # format in-place black --check . # dry-run (what CI runs) -
No other linters (flake8, pylint, mypy) are currently configured in CI.
Testing Patterns#
The test suite uses pytest with the mock library (not unittest.mock directly). Tests are organized per-package under <package>/<package>/tests/.
Key patterns to follow when writing new tests:
-
Module-level setup/teardown β use
setup_function()andteardown_function(), not pytest class fixtures or@pytest.fixture.def setup_function(): conf.override_all({}) # always reset config global mock_session mock_session = MagicMock() -
Global variables for fixtures β test files declare globals at the top and populate them in
setup_function(). -
MagicMockfor all external dependencies β replace Livy clients, HTTP sessions, IPython displays, etc. withMagicMock(). -
Reset configuration in every
setup_functionβconf.override_all({})clears all overrides; useconf.override(key, value)for specific keys. -
Plain
assertstatements β the codebase uses bare asserts, not pytest assertion helpers. -
No
conftest.pyβ there are no shared fixtures or plugins. Each test file is self-contained.
PR and Review Process#
Every pull request should satisfy the checklist in .github/pull_request_template.md :
- Write a clear description of what changed and why
- Run
black .and commit the result - Add a bullet point to the top of
CHANGELOG.md - Add or update unit tests
- Manually verify the change in a Jupyter notebook
- For new features: add an example notebook and/or update
README.md
Versioning#
- All three packages share a single synchronized version number β currently
0.23.0. - Versions live in each package's
__init__.py(__version__ = "0.23.0") and are managed exclusively by.bumpversion.cfg. Never edit version strings by hand. - Releases are triggered via GitHub Actions: Actions β Release workflow β Run workflow, choosing
patch,minor, ormajor. - The release workflow bumps versions, creates a git tag, generates a GitHub Release from
CHANGELOG.md, and then thepublish.ymlworkflow publishes all three packages to PyPI.
Gotchas & Non-Obvious Workflows#
1. Install order is mandatory#
autovizwidget depends on hdijupyterutils and sparkmagic depends on both. Always install bottom-up. Installing sparkmagic first will fail because its declared dependencies (hdijupyterutils>=0.6, autovizwidget>=0.6) will be pulled from PyPI, not your local editable versions.
# CORRECT order
pip install -e hdijupyterutils
pip install -e autovizwidget
pip install -e sparkmagic
# WRONG β do not do this
pip install -e sparkmagic # will pull hdijupyterutils from PyPI
2. ~/.sparkmagic/ must exist before running sparkmagic tests#
The sparkmagic package writes logs to ~/.sparkmagic/ and the test suite will error out if the directory is absent. CI creates it explicitly before running tests; you must do the same locally.
mkdir -p ~/.sparkmagic
pytest sparkmagic
3. Kerberos C headers are a hard build dependency#
requests-kerberos is in sparkmagic/requirements.txt and has a C extension that requires Kerberos development headers. On a fresh Linux machine, pip install -e sparkmagic will fail without them.
# Linux
sudo apt-get install -y libkrb5-dev
# macOS
brew install krb5
export LDFLAGS="-L/usr/local/opt/krb5/lib"
export CPPFLAGS="-I/usr/local/opt/krb5/include"
# Conda (avoids the issue entirely)
conda install requests-kerberos -y
4. Docker dev mode defaults to false#
The docker-compose.yml ships with args: dev_mode: "false", which installs sparkmagic from PyPI rather than your local code. You must manually change this to "true" when doing Docker-based development.
# docker-compose.yml β edit this section:
build:
args:
dev_mode: "true" # β change from "false"
After changing this, rebuild the image: docker-compose build jupyter.
5. Dockerfile.spark pins an old Spark + Python version#
Dockerfile.spark is based on datamechanics/spark:2.4.7-hadoop-3.1.0-java-8-scala-2.11-python-3.7-latest, which bundles Python 3.7 inside the Spark container and Scala 2.11. The reason is a Livy 0.7.x compatibility constraint with Scala 2.12. This means the Spark-side Python version is different from the 3.8+ requirement for the Python packages.
6. Root config.json is intentionally empty#
The config.json at the repository root is an empty placeholder ({}). The actual annotated example configuration is at sparkmagic/example_config.json.
7. Root pyproject.toml is for development only, not distribution#
The root Poetry project is named development and is only used to manage the dev virtual environment. The packages are published to PyPI from their individual setup.py files inside each sub-directory. Never run python -m build from the repo root expecting publishable artifacts β do it from within each package directory.
8. Kernel specs are not auto-registered on install#
pip install sparkmagic does not register the Jupyter kernel specs. You must run jupyter-kernelspec install for each kernel after installation. Without this step, the kernels will not appear in Jupyter's kernel picker.
9. No shared test fixtures β follow the module-level pattern#
There is no conftest.py anywhere in the repository. Pytest fixtures are not used. All new tests must follow the existing pattern: declare module-level globals, use setup_function() to initialize them, and use MagicMock for dependencies. Deviating from this pattern creates inconsistency.
10. All package versions must stay in sync#
.bumpversion.cfg keeps the three packages at the same version. Never manually edit __version__ in any __init__.py β always go through the GitHub Actions release workflow, which uses bumpversion to atomically update all three files and create the git tag.