AGENTS.md#
A concise guide for AI coding agents working in the sparkmagic repository.
Project Overview#
Sparkmagic is a set of tools for interactively working with remote Spark clusters in Jupyter notebooks. Rather than requiring a local Spark installation, sparkmagic communicates with a remote Spark cluster entirely through a REST server, meaning no Spark components need to be installed on the Jupyter server .
Three REST server backends are currently supported :
- Livy β for running interactive sessions on YARN
- Lighter β for YARN or Kubernetes (PySpark only)
- Ilum β for YARN or Kubernetes
Two Usage Modes#
- IPython Kernel magics β Load
sparkmagic.magicsin a regular IPython notebook and use the%spark/%%sparkcell magic to execute code against a remote cluster . - Dedicated Spark kernels β Select a PySpark, Spark (Scala), or SparkR kernel when creating a notebook. The kernel auto-connects to a preconfigured Livy endpoint and exposes magics like
%%sql,%%local,%%configure,%%info, and%%logs.
Key Features#
- Run Spark code in Python, Scala, and R against any remote Spark cluster
- Automatic
SparkContext(sc) andHiveContext(sqlContext) creation %%sqlmagic for SparkSQL with automatic Plotly-based visualization- Capture SQL output as Pandas DataFrames for local Python processing
- Send local files or DataFrames to the remote cluster
- Authentication via Basic Access, Kerberos, or custom authenticators
- Configurable retry policies and heartbeat monitoring for reliable remote sessions
Repository Structure#
The repository is a monorepo containing three independently installable Python packages :
| Package | Directory | Purpose |
|---|---|---|
sparkmagic | sparkmagic/ | Core magics, kernels, and Livy client |
autovizwidget | autovizwidget/ | Automatic Plotly-based DataFrame visualization |
hdijupyterutils | hdijupyterutils/ | Shared utilities: logging, configuration, events, IPython display |
Additional top-level resources include examples/ (sample notebooks), helm/ (Kubernetes Helm charts), and Docker files for local development .
Key Commands#
Installing Dependencies#
The three sub-packages must be installed in dependency order. Use editable installs for development so code changes are picked up immediately :
pip install -e hdijupyterutils
pip install -e autovizwidget
pip install -e sparkmagic
Alternatively, use Poetry to set up a fully isolated virtual environment from the root :
poetry install
# If numpy/pandas fail to install, run these first then retry:
# poetry run pip install numpy pandas
# poetry install
For Kerberos authentication support (required by CI and on Linux systems) :
sudo apt-get install -y libkrb5-dev
Running Tests#
Tests are organized per sub-package. The ~/.sparkmagic directory must exist before running the sparkmagic test suite :
pip install pytest mock
pytest hdijupyterutils
pytest autovizwidget
mkdir -p ~/.sparkmagic # Required for sparkmagic tests
pytest sparkmagic
With Poetry :
poetry run pytest
Linting#
The project enforces the Black code formatter. All PRs are checked automatically in CI :
# Check formatting (CI mode):
black --check .
# Auto-format:
black .
Installing Jupyter Kernels#
After installing sparkmagic, register the dedicated Spark kernels with Jupyter :
# Find install path first:
pip show sparkmagic
# Then cd to that path and run:
jupyter-kernelspec install sparkmagic/kernels/sparkkernel
jupyter-kernelspec install sparkmagic/kernels/pysparkkernel
jupyter-kernelspec install sparkmagic/kernels/sparkrkernel
Enabling the Server Extension#
The server extension enables programmatic cluster switching via the /reconnectsparkmagic API :
# Jupyter 7.x / JupyterLab 3.x+:
jupyter server extension enable --py sparkmagic
# Older Jupyter (5.2 or earlier) / JupyterLab 1 or 2:
jupyter serverextension enable --py sparkmagic
Docker Development Environment#
The Docker Compose setup spins up a full Jupyter + Livy + Spark stack for local development and testing :
docker compose build
docker compose up # Access Jupyter at http://localhost:8888
# Livy endpoint: http://spark:8998
docker compose down # Tear down containers
To test local code changes inside Docker, set dev_mode: true in docker-compose.yml before building. This installs all three sub-packages in editable mode inside the container.
Releasing#
Releases use the GitHub Actions Release workflow with semantic versioning. The steps are :
- Update
CHANGELOG.mdon themasterbranch - In GitHub Actions β Release workflow β Run workflow β select
patch | minor | major
Architecture#
Sparkmagic acts as a bridge between the local Jupyter environment and a remote Spark cluster, delegating all code execution to the cluster via a REST API .
βββββββββββββββββββββββββββββββ REST API βββββββββββββββββββββββ
β Jupyter Notebook β βββββββββββββββββββββββΆβ Livy / Lighter / β
β (magics or Spark kernel) ββββββββββββββββββββββββ β Ilum REST Server β
βββββββββββββββββββββββββββββββ JSON results/status ββββββββββββ¬βββββββββββ
β
ββββββββββββΌβββββββββββ
β Remote Spark β
β Cluster (YARN / β
β Kubernetes) β
βββββββββββββββββββββββ
Package Layout#
sparkmagic/ β Core Package #
sparkmagic/sparkmagic/
βββ magics/ # IPython magic implementation
β βββ remotesparkmagics.py # %spark magic (RemoteSparkMagics)
β βββ sparkmagicsbase.py # Shared base class (SparkMagicBase)
βββ kernels/ # Dedicated Spark kernel wrappers
β βββ kernelmagics.py # Kernel-specific magics (%%spark, %%sql, %%local, etc.)
β βββ pysparkkernel/ # PySpark kernel
β βββ sparkkernel/ # Scala kernel
β βββ sparkrkernel/ # SparkR kernel
β βββ wrapperkernel/ # Generic wrapper kernel
βββ livyclientlib/ # REST client layer
β βββ sparkcontroller.py # Session orchestration & command routing
β βββ livysession.py # Remote session state, heartbeat thread
β βββ livyreliablehttpclient.py # HTTP client with retry policies
β βββ command.py # Wraps and executes user code statements
β βββ sqlquery.py # SQL query translation and execution
β βββ sessionmanager.py # Local session registry
β βββ endpoint.py # URL + auth credential holder
βββ auth/ # Authentication providers
β βββ customauth.py # Base Authenticator class (no-auth default)
β βββ basic.py # HTTP Basic Auth
β βββ kerberos.py # Kerberos Auth (via requests-kerberos)
βββ serverextension/ # Jupyter server extension
β βββ handlers.py # Tornado ReconnectHandler (/reconnectsparkmagic)
βββ controllerwidget/ # IPython widget UI for session management
βββ utils/ # Shared utilities
βββ configuration.py # Config loading, defaults, and overrides
βββ constants.py # UPPER_SNAKE_CASE constants (session kinds, etc.)
βββ sparkevents.py # Pluggable event emission system
βββ utils.py # Argument parsing, DataFrame conversion helpers
βββ dataframe_parser.py # Livy JSON β Pandas DataFrame conversion
βββ sparklogger.py # Logging setup
autovizwidget/ β Automatic Visualization #
Provides Plotly-based chart widgets that auto-render SQL query results. Contains plotlygraphs/ and widget/ sub-directories.
hdijupyterutils/ β Shared Utilities #
Provides configuration.py, events.py, ipythondisplay.py, ipywidgetfactory.py, log.py, and filesystem helpers shared by all three packages.
Key Execution Flow#
- User executes a
%%sparkcell or Spark kernel cell in Jupyter - The magic (or kernel magic) parses arguments and retrieves the
Endpoint(URL + auth) SparkControllerlooks up or creates aLivySessionfor that endpoint- User code is wrapped in a
Command(orSQLQueryfor%%sql) object LivyReliableHttpClientPOSTs the statement to Livy's/sessions/{id}/statementsendpoint- The client polls the statement status until completion, then fetches results
- Results (plain text, HTML tables, or image data) are rendered in the notebook output cell
Authentication Architecture#
All authenticators inherit from the Authenticator base class and implement __call__(request) to attach credentials to outgoing HTTP requests . The three built-in providers β None, Basic_Access, and Kerberos β are registered by dotted-path string in the config file . Custom authenticators can be added by registering their dotted path in ~/.sparkmagic/config.json .
Configuration System#
Runtime configuration is managed by sparkmagic/utils/configuration.py and loaded from ~/.sparkmagic/config.json. See sparkmagic/example_config.json for a fully annotated reference . Key configuration areas:
| Section | Notable Keys |
|---|---|
| Kernel credentials | kernel_python_credentials, kernel_scala_credentials, kernel_r_credentials |
| Session management | livy_session_startup_timeout_seconds (default: 60), wait_for_idle_timeout_seconds (15) |
| Heartbeat | heartbeat_refresh_seconds (30), heartbeat_retry_seconds (10) |
| Retry policy | retry_policy, retry_seconds_to_sleep_list, configurable_retry_policy_max_retries (8) |
| Results | max_results_sql (2500), pyspark_dataframe_encoding (utf-8), use_auto_viz |
| Session defaults | session_configs (user-replaceable), session_configs_defaults (explicit-override only) |
Conventions#
Code Style#
- Black is the enforced formatter. All code must pass
black --check .before merging. CI will fail on unformatted code . - There is no separate flake8/pylint configuration; Black is the sole style gate.
Naming Conventions#
- Constants use
UPPER_SNAKE_CASEand live insparkmagic/utils/constants.py. - Classes use
PascalCase(e.g.,LivySession,SparkController,RemoteSparkMagics). - Functions and variables use
snake_case. - Test files are named
test_*.pyand live in thetests/subdirectory of each package .
File Organization#
- Each of the three sub-packages (
sparkmagic,autovizwidget,hdijupyterutils) is self-contained with its ownsetup.py,setup.cfg,requirements.txt, andMANIFEST.in. - Shared logic between packages flows in one direction:
hdijupyterutilsβautovizwidgetβsparkmagic. Never add reverse dependencies. - All configuration keys and their default values are defined in
sparkmagic/utils/configuration.py. Add new config options here with sensible defaults. - Constants referenced across modules must be defined in
sparkmagic/utils/constants.pyrather than inlined as string literals .
Testing Conventions#
Tests use pytest with the mock library . The standard pattern is:
from mock import MagicMock
import pytest
import sparkmagic.utils.configuration as conf
def setup_function():
# Reset config state before every test
conf.override_all({})
# Initialize shared mock objects here
def test_something():
# Arrange
mock_client = MagicMock()
# Act
result = some_function(mock_client)
# Assert
assert result == expected
mock_client.some_method.assert_called_once_with(expected_arg)
Key testing conventions :
- Use module-level
setup_function()(not class-based setUp) to initialize state before each test. - Always call
conf.override_all({})insetup_function()to prevent config state from leaking between tests. - Use
MagicMockfor test doubles; verify interactions withassert_called_once_with()and similar methods. - Use standard Python
assertstatements (notassertEqual-style).
Pull Request Conventions#
PRs should follow this checklist :
- Write a clear description of the changes
- Format code with
black - Add an entry to
CHANGELOG.mdunder the "Next Release" section - Add or modify unit tests to cover the change
- Manually test with a notebook if the change affects user-facing behavior
- Update documentation if the change adds a new feature
Changelog and Versioning#
- The
CHANGELOG.mdhas a permanent "Next Release" section at the top for unreleased changes . - Each release section is organized under
### Updatesand### Bug Fixesheadings. - The project follows semantic versioning (
MAJOR.MINOR.PATCH). Version is single-sourced fromsparkmagic/sparkmagic/__init__.pyand managed with.bumpversion.cfg. - Releases are cut via the GitHub Actions Release workflow β do not bump the version manually .
Configuration Override Pattern#
To programmatically override config in notebooks or tests :
import sparkmagic.utils.configuration as conf
# Override a single key:
conf.override('max_results_sql', 5000)
# Override multiple keys at once (useful in tests):
conf.override_all({'max_results_sql': 5000, 'use_auto_viz': False})
Gotchas#
1. ~/.sparkmagic directory must exist#
Sparkmagic expects the directory ~/.sparkmagic to exist at runtime and will fail if it does not. The CI workflow explicitly creates it before running the test suite . Always run:
mkdir -p ~/.sparkmagic
Place your config.json there. Use sparkmagic/example_config.json as a starting template .
2. All code executes remotely β %%local for client-side work#
No Spark runs locally. Every cell sent through %%spark or a Spark kernel is serialized and executed on the remote driver via Livy . This means:
- You cannot directly inspect Python objects from a Spark cell in local Python scope.
- Use the
%%localmagic to execute code in the local IPython kernel. - Structured data returned from Spark is serialized to JSON by sparkmagic before arriving in the notebook; large result sets can be slow.
3. pandas is pinned to < 3.0.0#
The sparkmagic/requirements.txt pins pandas<3.0.0 . Do not upgrade past this limit without verifying DataFrame parsing compatibility throughout livyclientlib/ and autovizwidget/.
4. Kerberos requires libkrb5-dev and an active ticket#
Installing requests_kerberos is not enough. The system package libkrb5-dev must be installed (sudo apt-get install -y libkrb5-dev) and a valid Kerberos ticket must be present (via kinit) before sparkmagic can authenticate . The CI workflow installs libkrb5-dev as an explicit step .
5. ignore_ssl_errors defaults to false β keep it that way#
The config key ignore_ssl_errors is false by default . Setting it to true disables certificate verification for all Livy requests. Never enable this in production environments.
6. conf.override() must be called before %load_ext sparkmagic.magics#
Some configuration values (e.g., cleanup_all_sessions_on_exit) are read once during sparkmagic initialization. Overriding them after the extension is loaded has no effect :
# β
Correct order:
import sparkmagic.utils.configuration as conf
conf.override('cleanup_all_sessions_on_exit', True)
%load_ext sparkmagic.magics
# β Too late β override is ignored:
%load_ext sparkmagic.magics
conf.override('cleanup_all_sessions_on_exit', True)
7. VSCode-Jupyter bootstrapping code must not be wrapped in %%spark#
When using VS Code's Jupyter extension, the runtime may inject bootstrapping code containing _VSCODE_ markers into a cell. If sparkmagic wraps this code and sends it to Livy, the notebook hangs indefinitely . Any code-injection logic that pre-processes cells must check for and exclude _VSCODE_-marked content.
8. Leading whitespace before magic commands causes misclassification#
A cell that starts with blank lines before a magic command (e.g., %%configure) can be misclassified by sparkmagic's parser, causing incorrect execution behavior . When writing code that programmatically constructs or submits cells, normalize leading whitespace before parsing.
9. session_configs vs. session_configs_defaults#
These two config keys behave differently when a user runs %%configure :
session_configs: The entire dictionary is replaced when the user changes any session setting.session_configs_defaults: Individual keys are only overridden when explicitly specified. Use this for settings (like Spark catalog type) that should persist unless the user takes deliberate action to change them.
10. Sub-package install order matters#
The three packages have a hard dependency chain: hdijupyterutils β autovizwidget β sparkmagic. Always install them in this order . Installing sparkmagic first will fail because hdijupyterutils and autovizwidget are not yet available.
11. Livy session startup timeout may need tuning#
The default livy_session_startup_timeout_seconds is 60 seconds . Slow or heavily loaded clusters may need a larger value. If you see spurious session-creation timeouts, increase this in ~/.sparkmagic/config.json.
12. Docker Livy endpoint is http://spark:8998, not localhost#
Inside the Docker Compose development environment, the Livy service is reachable at http://spark:8998 (the Docker Compose service name), not at http://localhost:8998 . Using localhost inside a notebook running in Docker will fail to connect.
13. Poetry numpy/pandas install may fail on first run#
On some platforms, poetry install can fail when resolving numpy or pandas. The documented workaround is :
poetry run pip install numpy pandas
poetry install
14. Python version support is 3.8β3.12#
The CI matrix tests Python 3.8 through 3.12 . Do not use language features or library APIs that require Python 3.13+. The pyproject.toml specifies python = "^3.8" .