jupyter_core
Shared base library for Jupyter config discovery, paths, and application scaffolding across the Jupyter ecosystem.
Repository Health
Technical Analysis
jupyter_core is the foundational package that nearly every Jupyter project depends on, even though it does very little on its own. It centralizes the platform-specific logic for locating configuration, data, and runtime directories (handling the differences between Windows, macOS, Homebrew, Linux/XDG, and legacy ~/.jupyter layouts) and exposes it as a small set of functions like jupyter_config_dir(), jupyter_data_dir(), and jupyter_path().
Beyond path resolution, it provides JupyterApp, a traitlets-based base class that every Jupyter command-line application (notebook servers, kernels, nbconvert, and more) inherits from for consistent config-file loading, logging, and CLI flag handling. It also ships the root jupyter command itself, which dispatches to jupyter-* subcommands discovered on PATH, plus a one-time migration tool for moving legacy IPython profile configuration into the modern Jupyter config layout.
Because dozens of independently maintained packages (JupyterLab, Jupyter Notebook, ipykernel, nbconvert, and others) all rely on jupyter_core’s path and application conventions, its own CI includes a dedicated “downstream” workflow that runs those projects’ test suites against jupyter_core’s main branch — catching breaking changes before they propagate through the ecosystem.
What You Get
- Cross-platform path resolution -
jupyter_config_dir(),jupyter_data_dir(), andjupyter_runtime_dir()handle Windows/macOS/Linux and Homebrew-vs-system conventions consistently. - Environment-aware search order -
jupyter_path()andjupyter_config_path()resolve precedence across environment variables, active virtualenv/conda environments, user directories, and system directories. - A shared application base class -
JupyterApp(built ontraitlets.config.Application) standardizes config-file loading, logging flags, and--generate-configbehavior for every Jupyter CLI tool. - The root
jupytercommand - discovers and dispatches to anyjupyter-*executable onPATH, and reports install paths viajupyter --paths. - Legacy-to-modern config migration -
jupyter migratemoves old IPython profile configuration into the current Jupyter config directory layout. - A troubleshooting report generator -
jupyter troubleshootcollects version, path, and environment info for bug reports.
Common Use Cases
- Building a new Jupyter-ecosystem CLI tool - subclass
JupyterAppto get consistent config loading, logging, and flag handling for free instead of reimplementing traitlets boilerplate. - Locating a user’s Jupyter config or data directory from a plugin - call
jupyter_config_dir()/jupyter_data_dir()instead of hardcoding~/.jupyterpaths that break on Windows or in virtualenvs. - Diagnosing an installation across mixed environments - run
jupyter --pathsorjupyter troubleshootto see exactly which config/data directories are being searched and in what order. - Migrating a user from classic IPython profiles to Jupyter’s config layout - run
jupyter migrateto move~/.ipythonprofile settings into~/.jupyterautomatically.
Under The Hood
Architecture
jupyter_core is organized as a small set of flat, sibling modules rather than a layered package: paths.py (config/data/runtime directory resolution using environment, virtualenv/conda, user, and system precedence rules), application.py (the JupyterApp base class extending traitlets.config.application.Application with Jupyter-specific aliases, flags, and config-file loading), command.py (the root jupyter CLI that discovers and dispatches to jupyter-* subcommands found on PATH), migrate.py (one-time migration of legacy ~/.ipython profile configuration into ~/.jupyter), and troubleshoot.py (a diagnostic report generator). There is no internal plugin system or dependency injection — consumers across the Jupyter ecosystem import individual functions and classes directly, so the package functions as a stable, minimal-surface-area contract; changing the search-order logic inside jupyter_path() would ripple through config resolution across every dependent Jupyter frontend and kernel.
Tech Stack
Pure Python 3.10+, built on platformdirs for OS-specific directory conventions and traitlets (Jupyter’s own configuration and type-validation framework) for the JupyterApp base class. The build backend is hatchling with the version read dynamically from jupyter_core/version.py. Dev and test tooling includes pytest (configured to also run --doctest-modules, executing docstring examples as tests, with warnings promoted to errors), pytest-cov, pytest-timeout, ruff for linting and formatting, mypy in strict mode, and pre-commit hooks. GitHub Actions runs CodeQL analysis, the main test suite, and a distinctive “downstream” workflow. Docs are built with Sphinx and the pydata-sphinx-theme, hosted on Read the Docs.
Code Quality
The test suite spans test_application.py, test_command.py, test_migrate.py, test_paths.py, test_troubleshoot.py, and test_utils.py, backed by a mocking.py helper module and fixture directories that simulate legacy IPython profile layouts for migration testing. Running doctests as part of the suite and treating warnings as errors sets an unusually strict bar for a small utility library. mypy runs in strict mode with disallow_untyped_defs and disallow_untyped_calls across the whole package, and the ruff configuration enables an extensive rule set including bandit-derived security checks, bugbear, pylint conventions, and pathlib-preference rules. The “downstream” CI job runs the test suites of dependent Jupyter projects against this repository’s main branch specifically to catch breaking API changes before they ship — a compatibility-testing pattern unusual for a package this size.
What Makes It Unique
jupyter_core doesn’t introduce novel technical concepts — its value is as connective tissue. It centralizes the fiddly, platform-specific logic (Windows vs. macOS vs. Homebrew vs. Linux/XDG conventions, virtualenv vs. conda vs. system Python precedence, legacy vs. modern config layouts) that every Jupyter frontend and kernel would otherwise reimplement inconsistently. The most distinctive piece of logic is the environment-precedence algorithm behind jupyter_path()/jupyter_config_path() (environment variable override, then active virtualenv/conda environment, then user directory, then system directory, with JUPYTER_PREFER_ENV_PATH as an explicit override), paired with the downstream-compatibility CI job that tests API changes against the dependent ecosystem across physically separate repositories.
Used by 2 apps in this directory
argilla
AI Development · Data Engineering
Collaborate on high-quality AI training data with a self-hosted annotation platform built for LLMs, NLP, and multimodal models.
GrowthBook
Developer Tools · Analytics · Monitoring
Open source feature flags, A/B testing, and warehouse-native experimentation that queries your existing data infrastructure—no data movement required.