jupyter_core

Shared base library for Jupyter config discovery, paths, and application scaffolding across the Jupyter ecosystem.

Library
PyPI
v5.9.1
216stars
BSD 3-Clause License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
64/100Good
Development Activity60
Maintenance32
Community84
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
75/100Good
Architecture78
Code Quality90
Innovation45
Learning Curve85

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(), and jupyter_runtime_dir() handle Windows/macOS/Linux and Homebrew-vs-system conventions consistently.
  • Environment-aware search order - jupyter_path() and jupyter_config_path() resolve precedence across environment variables, active virtualenv/conda environments, user directories, and system directories.
  • A shared application base class - JupyterApp (built on traitlets.config.Application) standardizes config-file loading, logging flags, and --generate-config behavior for every Jupyter CLI tool.
  • The root jupyter command - discovers and dispatches to any jupyter-* executable on PATH, and reports install paths via jupyter --paths.
  • Legacy-to-modern config migration - jupyter migrate moves old IPython profile configuration into the current Jupyter config directory layout.
  • A troubleshooting report generator - jupyter troubleshoot collects version, path, and environment info for bug reports.

Common Use Cases

  • Building a new Jupyter-ecosystem CLI tool - subclass JupyterApp to 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 ~/.jupyter paths that break on Windows or in virtualenvs.
  • Diagnosing an installation across mixed environments - run jupyter --paths or jupyter troubleshoot to 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 migrate to move ~/.ipython profile settings into ~/.jupyter automatically.

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.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search