Full Code of jupyter/nbclient for AI

main e500e95e01a0 cached
69 files
302.2 KB
86.6k tokens
175 symbols
1 requests
Download .txt
Showing preview only (321K chars total). Download the full file or copy to clipboard to get everything.
Repository: jupyter/nbclient
Branch: main
Commit: e500e95e01a0
Files: 69
Total size: 302.2 KB

Directory structure:
gitextract_xrhwfx_4/

├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── check-release.yml
│       ├── main.yml
│       ├── prep-release.yml
│       ├── publish-changelog.yml
│       └── publish-release.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .readthedocs.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── RELEASING.md
├── binder/
│   ├── empty_notebook.ipynb
│   ├── environment.yml
│   └── run_nbclient.ipynb
├── docs/
│   ├── Makefile
│   ├── UPDATE.md
│   ├── _static/
│   │   └── custom.css
│   ├── autogen_config.py
│   ├── client.rst
│   ├── conf.py
│   ├── index.rst
│   ├── installation.rst
│   ├── make.bat
│   └── reference/
│       ├── index.rst
│       ├── modules.rst
│       └── nbclient.rst
├── nbclient/
│   ├── __init__.py
│   ├── _version.py
│   ├── cli.py
│   ├── client.py
│   ├── exceptions.py
│   ├── jsonutil.py
│   ├── output_widget.py
│   ├── py.typed
│   └── util.py
├── pyproject.toml
└── tests/
    ├── __init__.py
    ├── base.py
    ├── conftest.py
    ├── fake_kernelmanager.py
    ├── files/
    │   ├── Autokill.ipynb
    │   ├── Check History in Memory.ipynb
    │   ├── Clear Output.ipynb
    │   ├── Disable Stdin.ipynb
    │   ├── Empty Cell.ipynb
    │   ├── Error.ipynb
    │   ├── Factorials.ipynb
    │   ├── HelloWorld.ipynb
    │   ├── Inline Image.ipynb
    │   ├── Interrupt.ipynb
    │   ├── JupyterWidgets.ipynb
    │   ├── Other Comms.ipynb
    │   ├── Output.ipynb
    │   ├── Parallel Execute A.ipynb
    │   ├── Parallel Execute B.ipynb
    │   ├── SVG.ipynb
    │   ├── Skip Exceptions with Cell Tags.ipynb
    │   ├── Skip Exceptions.ipynb
    │   ├── Skip Execution with Cell Tag.ipynb
    │   ├── Sleep1s.ipynb
    │   ├── Unicode.ipynb
    │   ├── UnicodePy3.ipynb
    │   └── update-display-id.ipynb
    ├── test_cli.py
    ├── test_client.py
    └── test_util.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"
    groups:
      actions:
        patterns:
          - "*"
  - package-ecosystem: "pip"
    directory: "/"
    schedule:
      interval: "daily"
      time: "04:00"
    groups:
      actions:
        patterns:
          - "*"


================================================
FILE: .github/workflows/check-release.yml
================================================
name: Check Release
on:
  push:
    branches: ["*"]
  pull_request:
    branches: ["*"]
  release:
    types: [published]
  schedule:
    - cron: "0 0 * * *"

jobs:
  check_release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
      - name: Install Dependencies
        shell: bash
        run: |
          pip install -e .
      - name: Check Release
        uses: jupyter-server/jupyter_releaser/.github/actions/check-release@v2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}


================================================
FILE: .github/workflows/main.yml
================================================
name: CI

on:
  push:
    branches: ["main"]
  pull_request:
  workflow_dispatch:
  schedule:
    - cron: "0 8 * * *"

defaults:
  run:
    shell: bash -eux {0}

jobs:
  test_lint:
    name: Test Lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
      - name: Run Linters
        run: |
          hatch run typing:test
          hatch run lint:build
          pipx run interrogate -v .
          pipx run doc8 --max-line-length=200

  test_docs:
    name: Test Docs
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
      - run: hatch run docs:build

  build:
    name: Build, test and code coverage
    runs-on: ${{ matrix.os }}
    timeout-minutes: 30
    strategy:
      fail-fast: false
      matrix:
        os: ["ubuntu-latest", "macos-latest", "windows-latest"]
        python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
        include:
          - os: ubuntu-latest
            python-version: "pypy-3.11"
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Base Setup
        uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
      - name: Run the tests
        run: hatch run cov:test || hatch run test:test --lf
      - uses: jupyterlab/maintainer-tools/.github/actions/upload-coverage@v1

  coverage:
    runs-on: ubuntu-latest
    needs:
      - build
    steps:
      - uses: actions/checkout@v4
      - uses: jupyterlab/maintainer-tools/.github/actions/report-coverage@v1

  test_minimum_versions:
    name: Test Minimum Versions
    timeout-minutes: 20
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
        with:
          dependency_type: minimum
      - name: Run the unit tests
        run: |
          hatch run test:nowarn || hatch -v run test:nowarn --lf

  test_prereleases:
    name: Test Prereleases
    runs-on: ubuntu-latest
    timeout-minutes: 20
    steps:
      - uses: actions/checkout@v4
      - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
        with:
          dependency_type: pre
      - name: Run the tests
        run: |
          hatch run test:nowarn || hatch run test:nowarn --lf

  make_sdist:
    name: Make SDist
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v4
      - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
      - uses: jupyterlab/maintainer-tools/.github/actions/make-sdist@v1

  test_sdist:
    runs-on: ubuntu-latest
    needs: [make_sdist]
    name: Install from SDist and Test
    timeout-minutes: 20
    steps:
      - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
      - uses: jupyterlab/maintainer-tools/.github/actions/test-sdist@v1
        with:
          package_spec: .
          test_command: hatch run test:test || hatch run test:test --lf

  check_links:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
      - uses: jupyterlab/maintainer-tools/.github/actions/check-links@v1

  check_release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
      - name: Check Release
        uses: jupyter-server/jupyter_releaser/.github/actions/check-release@v2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}

  tests_check: # This job does nothing and is only used for the branch protection
    if: always()
    needs:
      - coverage
      - test_lint
      - test_docs
      - test_minimum_versions
      - test_prereleases
      - check_links
      - check_release
      - test_sdist
    runs-on: ubuntu-latest
    steps:
      - name: Decide whether the needed jobs succeeded or failed
        uses: re-actors/alls-green@release/v1
        with:
          jobs: ${{ toJSON(needs) }}


================================================
FILE: .github/workflows/prep-release.yml
================================================
name: "Step 1: Prep Release"
on:
  workflow_dispatch:
    inputs:
      version_spec:
        description: "New Version Specifier"
        default: "next"
        required: false
      branch:
        description: "The branch to target"
        required: false
      post_version_spec:
        description: "Post Version Specifier"
        required: false
      silent:
        description: "Set a placeholder in the changelog and don't publish the release."
        required: false
        type: boolean
      since:
        description: "Use PRs with activity since this date or git reference"
        required: false
      since_last_stable:
        description: "Use PRs with activity since the last stable git tag"
        required: false
        type: boolean
jobs:
  prep_release:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1

      - name: Prep Release
        id: prep-release
        uses: jupyter-server/jupyter_releaser/.github/actions/prep-release@v2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          version_spec: ${{ github.event.inputs.version_spec }}
          silent: ${{ github.event.inputs.silent }}
          post_version_spec: ${{ github.event.inputs.post_version_spec }}
          target: ${{ github.event.inputs.target }}
          branch: ${{ github.event.inputs.branch }}
          since: ${{ github.event.inputs.since }}
          since_last_stable: ${{ github.event.inputs.since_last_stable }}

      - name: "** Next Step **"
        run: |
          echo "Optional): Review Draft Release: ${{ steps.prep-release.outputs.release_url }}"


================================================
FILE: .github/workflows/publish-changelog.yml
================================================
name: "Publish Changelog"
on:
  release:
    types: [published]

  workflow_dispatch:
    inputs:
      branch:
        description: "The branch to target"
        required: false

jobs:
  publish_changelog:
    runs-on: ubuntu-latest
    environment: release
    steps:
      - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1

      - uses: actions/create-github-app-token@v1
        id: app-token
        with:
          app-id: ${{ vars.APP_ID }}
          private-key: ${{ secrets.APP_PRIVATE_KEY }}

      - name: Publish changelog
        id: publish-changelog
        uses: jupyter-server/jupyter_releaser/.github/actions/publish-changelog@v2
        with:
          token: ${{ steps.app-token.outputs.token }}
          branch: ${{ github.event.inputs.branch }}

      - name: "** Next Step **"
        run: |
          echo "Merge the changelog update PR: ${{ steps.publish-changelog.outputs.pr_url }}"


================================================
FILE: .github/workflows/publish-release.yml
================================================
name: "Step 2: Publish Release"
on:
  workflow_dispatch:
    inputs:
      branch:
        description: "The target branch"
        required: false
      release_url:
        description: "The URL of the draft GitHub release"
        required: false
      steps_to_skip:
        description: "Comma separated list of steps to skip"
        required: false

jobs:
  publish_release:
    runs-on: ubuntu-latest
    environment: release
    permissions:
      id-token: write
    steps:
      - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1

      - uses: actions/create-github-app-token@v1
        id: app-token
        with:
          app-id: ${{ vars.APP_ID }}
          private-key: ${{ secrets.APP_PRIVATE_KEY }}

      - name: Populate Release
        id: populate-release
        uses: jupyter-server/jupyter_releaser/.github/actions/populate-release@v2
        with:
          token: ${{ steps.app-token.outputs.token }}
          branch: ${{ github.event.inputs.branch }}
          release_url: ${{ github.event.inputs.release_url }}
          steps_to_skip: ${{ github.event.inputs.steps_to_skip }}

      - name: Finalize Release
        id: finalize-release
        uses: jupyter-server/jupyter_releaser/.github/actions/finalize-release@v2
        with:
          token: ${{ steps.app-token.outputs.token }}
          release_url: ${{ steps.populate-release.outputs.release_url }}

      - name: "** Next Step **"
        if: ${{ success() }}
        run: |
          echo "Verify the final release"
          echo ${{ steps.finalize-release.outputs.release_url }}

      - name: "** Failure Message **"
        if: ${{ failure() }}
        run: |
          echo "Failed to Publish the Draft Release Url:"
          echo ${{ steps.populate-release.outputs.release_url }}


================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# Autogenerated docs
docs/reference/config_options.rst
docs/changelog.md

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/
docs/changelog.md

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version

# pipenv
#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
#   However, in case of collaboration, if having platform-specific dependencies or dependencies
#   having no cross-platform support, pipenv may install dependencies that don't work, or not
#   install all needed dependencies.
Pipfile
Pipfile.lock

# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# Pycharm stuff
.idea/

# VSCode
.vscode


================================================
FILE: .pre-commit-config.yaml
================================================
ci:
  autoupdate_schedule: monthly
  autoupdate_commit_msg: "chore: update pre-commit hooks"

repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.5.0
    hooks:
      - id: check-case-conflict
      - id: check-ast
      - id: check-docstring-first
      - id: check-executables-have-shebangs
      - id: check-added-large-files
      - id: check-case-conflict
      - id: check-merge-conflict
      - id: check-json
      - id: check-toml
      - id: check-yaml
      - id: debug-statements
      - id: end-of-file-fixer
      - id: trailing-whitespace

  - repo: https://github.com/python-jsonschema/check-jsonschema
    rev: 0.27.4
    hooks:
      - id: check-github-workflows

  - repo: https://github.com/executablebooks/mdformat
    rev: 0.7.17
    hooks:
      - id: mdformat
        additional_dependencies:
          [mdformat-gfm, mdformat-frontmatter, mdformat-footnote]

  - repo: https://github.com/pre-commit/mirrors-prettier
    rev: "v4.0.0-alpha.8"
    hooks:
      - id: prettier
        types_or: [yaml, html, json]

  - repo: https://github.com/adamchainz/blacken-docs
    rev: "1.16.0"
    hooks:
      - id: blacken-docs
        additional_dependencies: [black==23.7.0]

  - repo: https://github.com/codespell-project/codespell
    rev: "v2.2.6"
    hooks:
      - id: codespell
        args: ["-L", "sur,nd"]

  - repo: https://github.com/pre-commit/pygrep-hooks
    rev: "v1.10.0"
    hooks:
      - id: rst-backticks
      - id: rst-directive-colons
      - id: rst-inline-touching-normal

  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: "v1.19.0"
    hooks:
      - id: mypy
        files: "^nbclient"
        stages: [manual]
        args: ["--install-types", "--non-interactive"]
        additional_dependencies:
          [
            "traitlets>=5.13",
            "jupyter_core>=5.3.2",
            "jupyter_client",
            "nbformat",
          ]

  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.2.0
    hooks:
      - id: ruff
        types_or: [python, jupyter]
        args: ["--fix", "--show-fixes"]
        exclude: "^tests/files/.*.ipynb"
      - id: ruff-format
        types_or: [python, jupyter]
        exclude: "^tests/files/.*.ipynb"

  - repo: https://github.com/scientific-python/cookie
    rev: "2024.01.24"
    hooks:
      - id: sp-repo-review
        additional_dependencies: ["repo-review[cli]"]


================================================
FILE: .readthedocs.yml
================================================
# .readthedocs.yml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details

# Required
version: 2

sphinx:
  configuration: docs/conf.py

formats: all

python:
  install:
    - method: pip
      path: .
      extra_requirements:
        - docs

build:
  os: ubuntu-22.04
  tools:
    python: "3.11"


================================================
FILE: CHANGELOG.md
================================================
# Changes in NBClient {#changelog}

<!-- <START NEW CHANGELOG ENTRY> -->

## 0.10.4

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.10.3...af9b77a952b78b2bd548945471114315d202afbf))

### Merged PRs

- Allow `display_id` to be `None` [#338](https://github.com/jupyter/nbclient/pull/338) ([@davidbrochart](https://github.com/davidbrochart), [@YannickJadoul](https://github.com/YannickJadoul), [@slayoo](https://github.com/slayoo))

### Contributors to this release

The following people contributed discussions, new ideas, code and documentation contributions, and review.
See [our definition of contributors](https://github-activity.readthedocs.io/en/latest/#how-does-this-tool-define-contributions-in-the-reports).

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2025-12-19&to=2025-12-23&type=c))

@davidbrochart ([activity](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adavidbrochart+updated%3A2025-12-19..2025-12-23&type=Issues)) | @slayoo ([activity](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Aslayoo+updated%3A2025-12-19..2025-12-23&type=Issues)) | @YannickJadoul ([activity](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3AYannickJadoul+updated%3A2025-12-19..2025-12-23&type=Issues))

<!-- <END NEW CHANGELOG ENTRY> -->

## 0.10.3

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.10.2...b42ad03acc0bb1ed26db65ab72ac617679cbbb62))

### Merged PRs

- Drop Python 3.9, test 3.14 and pypy-3.11 [#337](https://github.com/jupyter/nbclient/pull/337) ([@davidbrochart](https://github.com/davidbrochart))
- correct execution count in test for ipython 9.8 and above [#335](https://github.com/jupyter/nbclient/pull/335) ([@drorspei](https://github.com/drorspei), [@davidbrochart](https://github.com/davidbrochart))
- Update contribution with pytest instructions, remove tox. [#331](https://github.com/jupyter/nbclient/pull/331) ([@dgrahn](https://github.com/dgrahn), [@davidbrochart](https://github.com/davidbrochart))

### Contributors to this release

The following people contributed discussions, new ideas, code and documentation contributions, and review.
See [our definition of contributors](https://github-activity.readthedocs.io/en/latest/#how-does-this-tool-define-contributions-in-the-reports).

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2024-12-19&to=2025-12-19&type=c))

@davidbrochart ([activity](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adavidbrochart+updated%3A2024-12-19..2025-12-19&type=Issues)) | @dgrahn ([activity](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adgrahn+updated%3A2024-12-19..2025-12-19&type=Issues)) | @drorspei ([activity](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adrorspei+updated%3A2024-12-19..2025-12-19&type=Issues))

## 0.10.2

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.10.1...5a68cb361412d540e23fbc444cda75ede73c16d1))

### Merged PRs

- Drop Python 3.8, test PyPy 3.10 [#323](https://github.com/jupyter/nbclient/pull/323) ([@davidbrochart](https://github.com/davidbrochart))
- Gracefully handle explicit transient=None [#322](https://github.com/jupyter/nbclient/pull/322) ([@callmephilip](https://github.com/callmephilip))

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2024-11-29&to=2024-12-19&type=c))

[@callmephilip](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Acallmephilip+updated%3A2024-11-29..2024-12-19&type=Issues) | [@davidbrochart](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adavidbrochart+updated%3A2024-11-29..2024-12-19&type=Issues)

## 0.10.1

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.10.0...e6df5842d471047afcb45595d95a39be549896e4))

### Maintenance and upkeep improvements

- Run docs on ubuntu [#314](https://github.com/jupyter/nbclient/pull/314) ([@blink1073](https://github.com/blink1073))

### Other merged PRs

- avoid deprecation warning for py313 [#320](https://github.com/jupyter/nbclient/pull/320) ([@lucascolley](https://github.com/lucascolley))

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2024-03-13&to=2024-11-29&type=c))

[@blink1073](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Ablink1073+updated%3A2024-03-13..2024-11-29&type=Issues) | [@davidbrochart](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adavidbrochart+updated%3A2024-03-13..2024-11-29&type=Issues) | [@lucascolley](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Alucascolley+updated%3A2024-03-13..2024-11-29&type=Issues)

## 0.10.0

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.9.1...3286ae09f41d04fd3354519582750775abc034e5))

### Enhancements made

- Optionally write out executed notebook in jupyter-execute [#307](https://github.com/jupyter/nbclient/pull/307) ([@wpk-nist-gov](https://github.com/wpk-nist-gov))

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2024-03-12&to=2024-03-12&type=c))

[@blink1073](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Ablink1073+updated%3A2024-03-12..2024-03-12&type=Issues) | [@wpk-nist-gov](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Awpk-nist-gov+updated%3A2024-03-12..2024-03-12&type=Issues)

## 0.9.1

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.9.0...6f6aa8cb1a853c81975fcc48fa5cfcc3d37bcddd))

### Maintenance and upkeep improvements

- Update Release Scripts [#309](https://github.com/jupyter/nbclient/pull/309) ([@blink1073](https://github.com/blink1073))
- Pin to Pytest 7 [#308](https://github.com/jupyter/nbclient/pull/308) ([@blink1073](https://github.com/blink1073))

### Other merged PRs

- chore: update pre-commit hooks [#305](https://github.com/jupyter/nbclient/pull/305) ([@pre-commit-ci](https://github.com/pre-commit-ci))
- chore: update pre-commit hooks [#304](https://github.com/jupyter/nbclient/pull/304) ([@pre-commit-ci](https://github.com/pre-commit-ci))
- chore: update pre-commit hooks [#303](https://github.com/jupyter/nbclient/pull/303) ([@pre-commit-ci](https://github.com/pre-commit-ci))
- Bump actions/checkout from 3 to 4 [#302](https://github.com/jupyter/nbclient/pull/302) ([@dependabot](https://github.com/dependabot))
- chore: update pre-commit hooks [#300](https://github.com/jupyter/nbclient/pull/300) ([@pre-commit-ci](https://github.com/pre-commit-ci))

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2023-11-07&to=2024-03-12&type=c))

[@blink1073](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Ablink1073+updated%3A2023-11-07..2024-03-12&type=Issues) | [@dependabot](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adependabot+updated%3A2023-11-07..2024-03-12&type=Issues) | [@pre-commit-ci](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Apre-commit-ci+updated%3A2023-11-07..2024-03-12&type=Issues)

## 0.9.0

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.8.0...31cf1e751935628b2ce8b88b7c00e5b53e9dcfd6))

### Maintenance and upkeep improvements

- Use jupyter releaser [#301](https://github.com/jupyter/nbclient/pull/301) ([@blink1073](https://github.com/blink1073))
- Clean up lint and move tests out of source [#299](https://github.com/jupyter/nbclient/pull/299) ([@blink1073](https://github.com/blink1073))
- Adopt ruff format [#298](https://github.com/jupyter/nbclient/pull/298) ([@blink1073](https://github.com/blink1073))
- Update typings for mypy 1.6 [#297](https://github.com/jupyter/nbclient/pull/297) ([@blink1073](https://github.com/blink1073))
- Adopt sp-repo-review [#295](https://github.com/jupyter/nbclient/pull/295) ([@blink1073](https://github.com/blink1073))
- Fix lint error [#289](https://github.com/jupyter/nbclient/pull/289) ([@blink1073](https://github.com/blink1073))

### Other merged PRs

- Bump actions/checkout from 3 to 4 [#293](https://github.com/jupyter/nbclient/pull/293) ([@dependabot](https://github.com/dependabot))

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2023-05-22&to=2023-11-07&type=c))

[@blink1073](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Ablink1073+updated%3A2023-05-22..2023-11-07&type=Issues) | [@dependabot](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adependabot+updated%3A2023-05-22..2023-11-07&type=Issues) | [@pre-commit-ci](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Apre-commit-ci+updated%3A2023-05-22..2023-11-07&type=Issues)

## 0.8.0

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.7.4...cb7b4f7f409bbd06d55cc339afdcdea79da0e199))

### Maintenance and upkeep improvements

- Bump min version support [#287](https://github.com/jupyter/nbclient/pull/287) ([@blink1073](https://github.com/blink1073))

### Other merged PRs

- Bump actions/checkout from 2 to 3 [#275](https://github.com/jupyter/nbclient/pull/275) ([@dependabot](https://github.com/dependabot))

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2023-04-25&to=2023-05-22&type=c))

[@blink1073](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Ablink1073+updated%3A2023-04-25..2023-05-22&type=Issues) | [@dependabot](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adependabot+updated%3A2023-04-25..2023-05-22&type=Issues) | [@pre-commit-ci](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Apre-commit-ci+updated%3A2023-04-25..2023-05-22&type=Issues)

## 0.7.4

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.7.3...20b7d4b6eef33ccd1bbd8d346a7a75522ac67d75))

### Enhancements made

- include stream output in CellExecutionError [#282](https://github.com/jupyter/nbclient/pull/282) ([@minrk](https://github.com/minrk))

### Bugs fixed

- avoid duplicate 'Exception: message' in CellExecutionError [#283](https://github.com/jupyter/nbclient/pull/283) ([@minrk](https://github.com/minrk))

### Maintenance and upkeep improvements

- Use local coverage [#281](https://github.com/jupyter/nbclient/pull/281) ([@blink1073](https://github.com/blink1073))

### Other merged PRs

- Send KeyboardInterrupt a little later in test_run_all_notebooks\[Interrupt.ipynb-opts6\] [#285](https://github.com/jupyter/nbclient/pull/285) ([@kxxt](https://github.com/kxxt))

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2023-04-03&to=2023-04-25&type=c))

[@blink1073](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Ablink1073+updated%3A2023-04-03..2023-04-25&type=Issues) | [@davidbrochart](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adavidbrochart+updated%3A2023-04-03..2023-04-25&type=Issues) | [@kxxt](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Akxxt+updated%3A2023-04-03..2023-04-25&type=Issues) | [@minrk](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Aminrk+updated%3A2023-04-03..2023-04-25&type=Issues) | [@pre-commit-ci](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Apre-commit-ci+updated%3A2023-04-03..2023-04-25&type=Issues)

## 0.7.3

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.7.2...aa62bc79274d264e9b9d70a139c9506a740b5d77))

### Maintenance and upkeep improvements

- Fix test stability [#276](https://github.com/jupyter/nbclient/pull/276) ([@blink1073](https://github.com/blink1073))
- Clean up license [#274](https://github.com/jupyter/nbclient/pull/274) ([@dcsaba89](https://github.com/dcsaba89))
- Update codecov link [#271](https://github.com/jupyter/nbclient/pull/271) ([@blink1073](https://github.com/blink1073))
- Add spelling and docstring enforcement [#269](https://github.com/jupyter/nbclient/pull/269) ([@blink1073](https://github.com/blink1073))
- Adopt ruff and address lint [#267](https://github.com/jupyter/nbclient/pull/267) ([@blink1073](https://github.com/blink1073))

### Other merged PRs

- Add coalesce_streams [#279](https://github.com/jupyter/nbclient/pull/279) ([@davidbrochart](https://github.com/davidbrochart))

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2022-11-29&to=2023-04-03&type=c))

[@blink1073](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Ablink1073+updated%3A2022-11-29..2023-04-03&type=Issues) | [@davidbrochart](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adavidbrochart+updated%3A2022-11-29..2023-04-03&type=Issues) | [@dcsaba89](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adcsaba89+updated%3A2022-11-29..2023-04-03&type=Issues) | [@pre-commit-ci](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Apre-commit-ci+updated%3A2022-11-29..2023-04-03&type=Issues)

## 0.7.2

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.7.1...e6f8b9f7001f9988a29bb011a0f6052987e6507a))

### Merged PRs

- Allow space after In [#264](https://github.com/jupyter/nbclient/pull/264) ([@davidbrochart](https://github.com/davidbrochart))
- Fix jupyter_core pinning [#263](https://github.com/jupyter/nbclient/pull/263) ([@davidbrochart](https://github.com/davidbrochart))
- Update README, add Python 3.11 [#260](https://github.com/jupyter/nbclient/pull/260) ([@davidbrochart](https://github.com/davidbrochart))

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2022-11-29&to=2022-11-29&type=c))

[@davidbrochart](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adavidbrochart+updated%3A2022-11-29..2022-11-29&type=Issues)

## 0.7.1

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.7.0...168340e8313e63fd9e037280f98ed22d47e2231b))

### Maintenance and upkeep improvements

- CI Refactor [#257](https://github.com/jupyter/nbclient/pull/257) ([@blink1073](https://github.com/blink1073))

### Other merged PRs

- Remove nest-asyncio [#259](https://github.com/jupyter/nbclient/pull/259) ([@davidbrochart](https://github.com/davidbrochart))
- Add upper bound to dependencies [#258](https://github.com/jupyter/nbclient/pull/258) ([@davidbrochart](https://github.com/davidbrochart))

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2022-10-06&to=2022-11-29&type=c))

[@blink1073](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Ablink1073+updated%3A2022-10-06..2022-11-29&type=Issues) | [@davidbrochart](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adavidbrochart+updated%3A2022-10-06..2022-11-29&type=Issues) | [@pre-commit-ci](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Apre-commit-ci+updated%3A2022-10-06..2022-11-29&type=Issues)

## 0.7.0

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.6.8...449f17d0374f43694d2203d216c97dd4ac7f2c0e))

### Maintenance and upkeep improvements

- Cleanup CI [#254](https://github.com/jupyter/nbclient/pull/254) ([@blink1073](https://github.com/blink1073))
- Handle client 8 support [#253](https://github.com/jupyter/nbclient/pull/253) ([@blink1073](https://github.com/blink1073))

### Other merged PRs

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2022-09-09&to=2022-10-06&type=c))

[@blink1073](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Ablink1073+updated%3A2022-09-09..2022-10-06&type=Issues) | [@pre-commit-ci](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Apre-commit-ci+updated%3A2022-09-09..2022-10-06&type=Issues)

## 0.6.8

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.6.7...f7d72b2c6937fc30add18b7413f89b691d1710be))

### Merged PRs

- Fix tests compatibility with IPython 8.5.0 [#251](https://github.com/jupyter/nbclient/pull/251) ([@frenzymadness](https://github.com/frenzymadness))

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2022-08-23&to=2022-09-09&type=c))

[@davidbrochart](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adavidbrochart+updated%3A2022-08-23..2022-09-09&type=Issues) | [@frenzymadness](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Afrenzymadness+updated%3A2022-08-23..2022-09-09&type=Issues)

## 0.6.7

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.6.6...979fb908dc133cc80a698c74d9b3d9d8af6c7bde))

### Merged PRs

- Fix tests for ipywidgets 8 [#246](https://github.com/jupyter/nbclient/pull/246) ([@frenzymadness](https://github.com/frenzymadness))
- \[pre-commit.ci\] pre-commit autoupdate [#236](https://github.com/jupyter/nbclient/pull/236) ([@pre-commit-ci](https://github.com/pre-commit-ci))

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2022-07-01&to=2022-08-23&type=c))

[@frenzymadness](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Afrenzymadness+updated%3A2022-07-01..2022-08-23&type=Issues) | [@pre-commit-ci](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Apre-commit-ci+updated%3A2022-07-01..2022-08-23&type=Issues)

## 0.6.6

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.6.5...b4a7cebf0238d4fbe814e19afbee8df3f610e80d))

### Merged PRs

- Start new client if needed in blocking setup_kernel [#241](https://github.com/jupyter/nbclient/pull/241) ([@davidbrochart](https://github.com/davidbrochart))

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2022-06-30&to=2022-07-01&type=c))

[@davidbrochart](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adavidbrochart+updated%3A2022-06-30..2022-07-01&type=Issues)

## 0.6.5

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.6.4...6aed8bec58d69004d3b6687c8bf589f175630f8d))

### Merged PRs

- Start new client if needed [#239](https://github.com/jupyter/nbclient/pull/239) ([@davidbrochart](https://github.com/davidbrochart))

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2022-05-31&to=2022-06-30&type=c))

[@davidbrochart](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adavidbrochart+updated%3A2022-05-31..2022-06-30&type=Issues)

## 0.6.4

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.6.3...01465b8d8597efa81f54f713ad3944fe963ab453))

### Merged PRs

- Make sure kernel is cleaned up in case an error occurred while starting kernel client [#234](https://github.com/jupyter/nbclient/pull/234) ([@CiprianAnton](https://github.com/CiprianAnton))
- Suppress most warnings in tests [#232](https://github.com/jupyter/nbclient/pull/232) ([@davidbrochart](https://github.com/davidbrochart))

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2022-05-09&to=2022-05-31&type=c))

[@CiprianAnton](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3ACiprianAnton+updated%3A2022-05-09..2022-05-31&type=Issues) | [@davidbrochart](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adavidbrochart+updated%3A2022-05-09..2022-05-31&type=Issues)

## 0.6.3

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.6.2...61d36ce423b00231833c737f59041f33d72a7bb3))

### Bugs fixed

- Clean up docs and typings [#230](https://github.com/jupyter/nbclient/pull/230) ([@blink1073](https://github.com/blink1073))

### Documentation improvements

- Clean up docs and typings [#230](https://github.com/jupyter/nbclient/pull/230) ([@blink1073](https://github.com/blink1073))

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2022-05-03&to=2022-05-09&type=c))

[@blink1073](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Ablink1073+updated%3A2022-05-03..2022-05-09&type=Issues) | [@chrisjsewell](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Achrisjsewell+updated%3A2022-05-03..2022-05-09&type=Issues) | [@davidbrochart](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adavidbrochart+updated%3A2022-05-03..2022-05-09&type=Issues) | [@meeseeksmachine](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Ameeseeksmachine+updated%3A2022-05-03..2022-05-09&type=Issues)

## 0.6.2

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.6.1...bd36f50a299fb2e0656386ec487c2bbc67a9a1c4))

### Merged PRs

- Fix documentation generation [#228](https://github.com/jupyter/nbclient/pull/228) ([@davidbrochart](https://github.com/davidbrochart))

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2022-05-03&to=2022-05-03&type=c))

[@davidbrochart](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adavidbrochart+updated%3A2022-05-03..2022-05-03&type=Issues)

## 0.6.1

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.6.0...571a65faa7b86bb647567373a529d9d8df38dd2f))

### Merged PRs

- \[pre-commit.ci\] pre-commit autoupdate [#225](https://github.com/jupyter/nbclient/pull/225) ([@pre-commit-ci](https://github.com/pre-commit-ci))
- Add error_on_interrupt trait [#224](https://github.com/jupyter/nbclient/pull/224) ([@davidbrochart](https://github.com/davidbrochart))
- Fix typo [#223](https://github.com/jupyter/nbclient/pull/223) ([@davidbrochart](https://github.com/davidbrochart))
- Add on_cell_executed hook [#222](https://github.com/jupyter/nbclient/pull/222) ([@davidbrochart](https://github.com/davidbrochart))

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2022-04-12&to=2022-05-03&type=c))

[@brichet](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Abrichet+updated%3A2022-04-12..2022-05-03&type=Issues) | [@davidbrochart](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adavidbrochart+updated%3A2022-04-12..2022-05-03&type=Issues) | [@pre-commit-ci](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Apre-commit-ci+updated%3A2022-04-12..2022-05-03&type=Issues)

## 0.6.0

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.5.13...295e0eee4a6b9c5c0ee0d490b4c4058a95c6cb79))

### Maintenance and upkeep improvements

- Fix typings and update mypy settings [#220](https://github.com/jupyter/nbclient/pull/220) ([@blink1073](https://github.com/blink1073))
- Add missing dep on testpath [#219](https://github.com/jupyter/nbclient/pull/219) ([@blink1073](https://github.com/blink1073))
- Add more pre-commit hooks and update flake8 [#218](https://github.com/jupyter/nbclient/pull/218) ([@blink1073](https://github.com/blink1073))

### Documentation improvements

- Clean up docs handling [#216](https://github.com/jupyter/nbclient/pull/216) ([@blink1073](https://github.com/blink1073))

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2022-03-11&to=2022-04-12&type=c))

[@blink1073](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Ablink1073+updated%3A2022-03-11..2022-04-12&type=Issues)

## 0.5.13

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.5.12...af2315aefbd8d08c1d6a473c289beef1e8ebbecb))

### Merged PRs

- Drop ipython_genutils [#209](https://github.com/jupyter/nbclient/pull/209) ([@davidbrochart](https://github.com/davidbrochart))

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2022-03-06&to=2022-03-11&type=c))

[@davidbrochart](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adavidbrochart+updated%3A2022-03-06..2022-03-11&type=Issues)

## 0.5.12

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.5.11...d20e29e803e5a22379f7a1356e7cf55d4649e9cb))

### Merged PRs

- Require traitlets>=5.0.0 [#204](https://github.com/jupyter/nbclient/pull/204) ([@davidbrochart](https://github.com/davidbrochart))
- Extend the ignored part of IPython outputs [#202](https://github.com/jupyter/nbclient/pull/202) ([@frenzymadness](https://github.com/frenzymadness))

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2022-02-14&to=2022-03-06&type=c))

[@davidbrochart](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adavidbrochart+updated%3A2022-02-14..2022-03-06&type=Issues) | [@frenzymadness](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Afrenzymadness+updated%3A2022-02-14..2022-03-06&type=Issues)

## 0.5.11

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.5.10...050c7da89a98159e6361b1ad0dbefd215db5f816))

### Merged PRs

- Pin ipython\<8 in tests [#198](https://github.com/jupyter/nbclient/pull/198) ([@davidbrochart](https://github.com/davidbrochart))
- Clear execution metadata, prefer msg header date when recording times [#195](https://github.com/jupyter/nbclient/pull/195) ([@kevin-bates](https://github.com/kevin-bates))
- Client hooks [#188](https://github.com/jupyter/nbclient/pull/188) ([@devintang3](https://github.com/devintang3))

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2022-01-13&to=2022-02-14&type=c))

[@davidbrochart](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adavidbrochart+updated%3A2022-01-13..2022-02-14&type=Issues) | [@devintang3](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adevintang3+updated%3A2022-01-13..2022-02-14&type=Issues) | [@kevin-bates](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Akevin-bates+updated%3A2022-01-13..2022-02-14&type=Issues)

## 0.5.10

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.5.9...e82c5d8d064ac1097f4e12f387b4c47ea5c576ff))

### Merged PRs

- Fix ipywidgets version in tests [#192](https://github.com/jupyter/nbclient/pull/192) ([@martinRenou](https://github.com/martinRenou))
- Compatibility with IPython 8 where tracebacks are different [#190](https://github.com/jupyter/nbclient/pull/190) ([@frenzymadness](https://github.com/frenzymadness))
- Drop tox [#187](https://github.com/jupyter/nbclient/pull/187) ([@davidbrochart](https://github.com/davidbrochart))
- Update README [#185](https://github.com/jupyter/nbclient/pull/185) ([@davidbrochart](https://github.com/davidbrochart))
- Drop python3.6, test python3.10 [#184](https://github.com/jupyter/nbclient/pull/184) ([@davidbrochart](https://github.com/davidbrochart))
- Fix typos [#182](https://github.com/jupyter/nbclient/pull/182) ([@kianmeng](https://github.com/kianmeng))
- Use codecov Github action v2 [#168](https://github.com/jupyter/nbclient/pull/168) ([@takluyver](https://github.com/takluyver))

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2021-11-19&to=2022-01-13&type=c))

[@davidbrochart](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adavidbrochart+updated%3A2021-11-19..2022-01-13&type=Issues) | [@frenzymadness](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Afrenzymadness+updated%3A2021-11-19..2022-01-13&type=Issues) | [@kianmeng](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Akianmeng+updated%3A2021-11-19..2022-01-13&type=Issues) | [@martinRenou](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3AmartinRenou+updated%3A2021-11-19..2022-01-13&type=Issues) | [@takluyver](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Atakluyver+updated%3A2021-11-19..2022-01-13&type=Issues)

## 0.5.9

([Full Changelog](https://github.com/jupyter/nbclient/compare/v0.5.8...0146681d7ffd62cbc675c8d1463a2b016a3d3008))

### Merged PRs

- Remove jupyter-run, keep jupyter-execute [#180](https://github.com/jupyter/nbclient/pull/180) ([@davidbrochart](https://github.com/davidbrochart))

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2021-11-12&to=2021-11-19&type=c))

[@davidbrochart](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adavidbrochart+updated%3A2021-11-12..2021-11-19&type=Issues)

## 0.5.8

No merged PRs

## 0.5.7

([Full Changelog](https://github.com/jupyter/nbclient/compare/0.5.6...d86c404536fb443898b631acaf29ce7ad88b06d9))

### Merged PRs

- Prepare for use with Jupyter Releaser [#175](https://github.com/jupyter/nbclient/pull/175) ([@davidbrochart](https://github.com/davidbrochart))

### Contributors to this release

([GitHub contributors page for this release](https://github.com/jupyter/nbclient/graphs/contributors?from=2021-11-12&to=2021-11-12&type=c))

[@davidbrochart](https://github.com/search?q=repo%3Ajupyter%2Fnbclient+involves%3Adavidbrochart+updated%3A2021-11-12..2021-11-12&type=Issues)

## 0.5.6

- Changed `jupyter execute` to `jupyter run` [#173](https://github.com/jupyter/nbclient/pull/173) ([@palewire](https://github.com/palewire))
- Move IPYKERNEL_CELL_NAME from tox to pytest [#172](https://github.com/jupyter/nbclient/pull/172) ([@frenzymadness](https://github.com/frenzymadness))

## 0.5.5

- Added CLI to README [#170](https://github.com/jupyter/nbclient/pull/170) ([@palewire](https://github.com/palewire))
- Add "jupyter execute" command-line interface [#165](https://github.com/jupyter/nbclient/pull/165) ([@palewire](https://github.com/palewire))
- Fix: updating buffers overwrote previous buffers [#169](https://github.com/jupyter/nbclient/pull/169) ([@maartenbreddels](https://github.com/maartenbreddels))
- Fix tests for ipykernel without debugpy [#166](https://github.com/jupyter/nbclient/pull/166) ([@frenzymadness](https://github.com/frenzymadness))
- gitignore Pipfile [#164](https://github.com/jupyter/nbclient/pull/164) ([@palewire](https://github.com/palewire))
- Fixed CONTRIBUTING.md link [#163](https://github.com/jupyter/nbclient/pull/163) ([@palewire](https://github.com/palewire))
- Fix typo [#162](https://github.com/jupyter/nbclient/pull/162) ([@The-Compiler](https://github.com/The-Compiler))
- Move format & lint to pre-commit [#161](https://github.com/jupyter/nbclient/pull/161) ([@chrisjsewell](https://github.com/chrisjsewell))
- Add `skip-execution` cell tag functionality [#151](https://github.com/jupyter/nbclient/pull/151) ([@chrisjsewell](https://github.com/chrisjsewell))

## 0.5.4

- Replace `km.cleanup` with `km.cleanup_resources` [#152](https://github.com/jupyter/nbclient/pull/152) ([@davidbrochart](https://github.com/davidbrochart))
- Use async generator backport only on old python [#154](https://github.com/jupyter/nbclient/pull/154) ([@mkoeppe](https://github.com/mkoeppe))
- Support parsing of IPython dev version [#150](https://github.com/jupyter/nbclient/pull/150) ([@cphyc](https://github.com/cphyc))
- Set `IPYKERNEL_CELL_NAME = <IPY-INPUT>` [#147](https://github.com/jupyter/nbclient/pull/147) ([@davidbrochart](https://github.com/davidbrochart))
- Print useful error message on exception [#142](https://github.com/jupyter/nbclient/pull/142) ([@certik](https://github.com/certik))

## 0.5.3

- Fix ipykernel's `stop_on_error` value to take into account `raises-exception` tag and `force_raise_errors` [#137](https://github.com/jupyter/nbclient/pull/137)

## 0.5.2

- Set minimum python version supported to 3.6.1 to avoid 3.6.0 issues
- CellExecutionError is now unpickleable
- Added testing for python 3.9
- Changed travis tests to github actions
- Documentation referencing an old model instead of NotebookClient was fixed
- `allow_error_names` option was added for a more specific scope of `allow_errors` to be applied

## 0.5.1

- Update kernel client class JIT if it's the synchronous version
- Several documentation fixes / improvements

## 0.5.0

- Move `language_info` retrieval before cell execution [#102](https://github.com/jupyter/nbclient/pull/102)
- HistoryManager setting for ipython kernels no longer applies twice (fix for 5.0 trailets release)
- Improved error handling around language_info missing
- `(async_)start_new_kernel_client` is now split into `(async_)start_new_kernel` and `(async_)start_new_kernel_client`

## 0.4.2 - 0.4.3

These patch releases were removed due to backwards incompatible changes that should have been a minor release.
If you were using these versions for the couple days they were up, move to 0.5.0 and you shouldn't have any issues.

## 0.4.1

- Python type hinting added to most interfaces! [#83](https://github.com/jupyter/nbclient/pull/83)
- Several documentation fixes and improvements were made [#86](https://github.com/jupyter/nbclient/pull/86)
- An asynchronous heart beat check was added to correctly raise a DeadKernelError when kernels die unexpectantly [#90](https://github.com/jupyter/nbclient/pull/90)

## 0.4.0

### Major Changes

- Use KernelManager's graceful shutdown rather than KILLing kernels [#64](https://github.com/jupyter/nbclient/pull/64)
- Mimic an Output widget at the frontend so that the Output widget behaves correctly [#68](https://github.com/jupyter/nbclient/pull/68)
- Nested asyncio is automatic, and works with Tornado [#71](https://github.com/jupyter/nbclient/pull/71)
- `async_execute` now has a `reset_kc` argument to control if the client is reset upon execution request [#53](https://github.com/jupyter/nbclient/pull/53)

### Fixes

- Fix `OSError: [WinError 6] The handle is invalid` for windows/python\<3.7 [#77](https://github.com/jupyter/nbclient/pull/77)
- Async wrapper Exceptions no longer loose their caused exception information [#65](https://github.com/jupyter/nbclient/pull/65)
- `extra_arguments` are now configurable by config settings [#66](https://github.com/jupyter/nbclient/pull/66)

### Operational

- Cross-OS testing now run on PRs via Github Actions [#63](https://github.com/jupyter/nbclient/pull/63)

## 0.3.1

### Fixes

- Check that a kernel manager exists before cleaning up the kernel [#61](https://github.com/jupyter/nbclient/pull/61)
- Force client class to be async when kernel manager is MultiKernelManager [#55](https://github.com/jupyter/nbclient/pull/55)
- Replace pip install with conda install in Binder [#54](https://github.com/jupyter/nbclient/pull/54)

## 0.3.0

### Major Changes

- The `(async_)start_new_kernel_client` method now supports starting a new client when its kernel manager (`self.km`) is a `MultiKernelManager`. The method now returns the kernel id in addition to the kernel client. If the kernel manager was a `KernelManager`, the returned kernel id is `None`. [#51](https://github.com/jupyter/nbclient/pull/51)
- Added sphinx-book-theme for documentation. Added a CircleCI job to let us preview the built documentation in a PR. [#50](https://github.com/jupyter/nbclient/pull/50)
- Added `reset_kc` option to `reset_execution_trackers`, so that the kernel client can be reset and a new one created in calls to `(async_)execute` [#44](https://github.com/jupyter/nbclient/pull/44)

### Docs

- Fixed documentation [#46](https://github.com/jupyter/nbclient/pull/46) [#47](https://github.com/jupyter/nbclient/pull/47)
- Added documentation status badge to the README
- Removed conda from documentation build

## 0.2.0

### Major Changes

- Async support is now available on the client. Methods that support async have an `async_` prefix and can be awaited [#10](https://github.com/jupyter/nbclient/pull/10) [#35](https://github.com/jupyter/nbclient/pull/35) [#37](https://github.com/jupyter/nbclient/pull/37) [#38](https://github.com/jupyter/nbclient/pull/38)
- Dropped support for Python 3.5 due to async compatibility issues [#34](https://github.com/jupyter/nbclient/pull/34)
- Notebook documents now include the [new kernel timing fields](https://github.com/jupyter/nbformat/pull/144) [#32](https://github.com/jupyter/nbclient/pull/32)

### Fixes

- Memory and process leaks from nbclient should now be fixed [#34](https://github.com/jupyter/nbclient/pull/34)
- Notebook execution exceptions now include error information in addition to the message [#41](https://github.com/jupyter/nbclient/pull/41)

### Docs

- Added [binder examples](https://mybinder.org/v2/gh/jupyter/nbclient/master?filepath=binder%2Frun_nbclient.ipynb) / tests [#7](https://github.com/jupyter/nbclient/pull/7)
- Added changelog to docs [#22](https://github.com/jupyter/nbclient/pull/22)
- Doc typo fixes [#27](https://github.com/jupyter/nbclient/pull/27) [#30](https://github.com/jupyter/nbclient/pull/30)

## 0.1.0

- Initial release -- moved out of nbconvert 6.0.0-a0


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing

We follow the [Jupyter Contribution Workflow](https://jupyter.readthedocs.io/en/latest/contributing/content-contributor.html) and the [IPython Contributing Guide](https://github.com/ipython/ipython/blob/master/CONTRIBUTING.md).

## Code formatting

Use the [pre-commit](https://pre-commit.com/) tool to format and lint the codebase:

```console
# to apply to only staged files
$ pre-commit run
# to run against all files
$ pre-commit run --all-files
# to install so that it is run before commits
$ pre-commit install
```

## Testing

Tests can be run through [`hatch`](https://hatch.pypa.io/) which will automatically manage test environments and dependencies.

```console
# to run all tests
$ hatch run test:test

# to run with coverage (used by CI)
$ hatch run cov:test
```

## Documentation

NbClient needs some PRs to copy over documentation!

## Releasing

If you are going to release a version of `nbclient` you should also be capable
of testing it and building the docs.

Please follow the instructions in [Testing](#testing) and [Documentation](#documentation) if
you are unfamiliar with how to do so.

The rest of the release process can be found in [these release instructions](./RELEASING.md).


================================================
FILE: LICENSE
================================================
BSD 3-Clause License

Copyright (c) 2020-, Jupyter Development Team

All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its
   contributors may be used to endorse or promote products derived from
   this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: README.md
================================================
[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/jupyter/nbclient/main?filepath=binder%2Frun_nbclient.ipynb)
[![Build Status](https://github.com/jupyter/nbclient/workflows/CI/badge.svg)](https://github.com/jupyter/nbclient/actions)
[![Documentation Status](https://readthedocs.org/projects/nbclient/badge/?version=latest)](https://nbclient.readthedocs.io/en/latest/?badge=latest)
[![Python 3.7](https://img.shields.io/badge/python-3.7-blue.svg)](https://www.python.org/downloads/release/python-370/)
[![Python 3.8](https://img.shields.io/badge/python-3.8-blue.svg)](https://www.python.org/downloads/release/python-380/)
[![Python 3.9](https://img.shields.io/badge/python-3.9-blue.svg)](https://www.python.org/downloads/release/python-390/)
[![Python 3.10](https://img.shields.io/badge/python-3.10-blue.svg)](https://www.python.org/downloads/release/python-3100/)
[![Python 3.11](https://img.shields.io/badge/python-3.11-blue.svg)](https://www.python.org/downloads/release/python-3110/)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)

# nbclient

**NBClient** lets you **execute** notebooks.

A client library for programmatic notebook execution, **NBClient** is a tool for running Jupyter Notebooks in
different execution contexts, including the command line.

## Interactive Demo

To demo **NBClient** interactively, click this Binder badge to start the demo:

[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/jupyter/nbclient/main?filepath=binder%2Frun_nbclient.ipynb)

## Installation

In a terminal, run:

```
python3 -m pip install nbclient
```

## Documentation

See [ReadTheDocs](https://nbclient.readthedocs.io/en/latest/) for more in-depth details about the project and the
[API Reference](https://nbclient.readthedocs.io/en/latest/reference/index.html).

## Python Version Support

This library currently supports Python 3.6+ versions. As minor Python
versions are officially sunset by the Python org, nbclient will similarly
drop support in the future.

## Origins

This library used to be part of the [nbconvert](https://nbconvert.readthedocs.io/en/latest/) project. NBClient extracted nbconvert's `ExecutePreprocessor`into its own library for easier updating and importing by downstream libraries and applications.

## Relationship to JupyterClient

NBClient and JupyterClient are distinct projects.

`jupyter_client` is a client library for the jupyter protocol. Specifically, `jupyter_client` provides the Python API
for starting, managing and communicating with Jupyter kernels.

While, nbclient allows notebooks to be run in different execution contexts.

## About the Jupyter Development Team

The Jupyter Development Team is the set of all contributors to the Jupyter project.
This includes all of the Jupyter subprojects.

The core team that coordinates development on GitHub can be found here:
https://github.com/jupyter/.

## Our Copyright Policy

Jupyter uses a shared copyright model. Each contributor maintains copyright
over their contributions to Jupyter. But, it is important to note that these
contributions are typically only changes to the repositories. Thus, the Jupyter
source code, in its entirety is not the copyright of any single person or
institution.  Instead, it is the collective copyright of the entire Jupyter
Development Team.  If individual contributors want to maintain a record of what
changes/contributions they have specific copyright on, they should indicate
their copyright in the commit message of the change, when they commit the
change to one of the Jupyter repositories.

With this in mind, the following banner should be used in any source code file
to indicate the copyright and license terms:

```
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
```


================================================
FILE: RELEASING.md
================================================
# Releasing

## Using `jupyter_releaser`

The recommended way to make a release is to use [`jupyter_releaser`](https://github.com/jupyter-server/jupyter_releaser#checklist-for-adoption).

## Manual Release

### Prerequisites

- First check that the CHANGELOG.md is up to date for the next release version
- Install packaging requirements: `pip install tbump build tomlkit==0.7.0`

### Bump version

- `export version=<NEW_VERSION>`
- `tbump ${version} --no-push`

### Push to GitHub

```bash
git push upstream && git push upstream --tags
```

### Push to PyPI

```bash
rm -rf dist/*
rm -rf build/*
python -m build .
twine upload dist/*
```


================================================
FILE: binder/empty_notebook.ipynb
================================================
{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Show a pandas dataframe"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import scrapbook as sb"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = pd.DataFrame(np.random.randn(20, 2), columns=[\"a\", \"b\"])\n",
    "data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Use scrapbook to store this data in the notebook\n",
    "sb.glue(\"dataframe\", data.to_dict())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Make a matplotlib plot"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Make and display a plot\n",
    "fig, ax = plt.subplots()\n",
    "ax.scatter(data[\"a\"], data[\"b\"])\n",
    "sb.glue(\"plot\", fig, \"display\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  },
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "state": {},
    "version_major": 2,
    "version_minor": 0
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}


================================================
FILE: binder/environment.yml
================================================
name: nbclient
channels:
  - conda-forge
dependencies:
  - numpy
  - pandas
  - matplotlib
  - scrapbook
  - nbformat
  - nbclient


================================================
FILE: binder/run_nbclient.ipynb
================================================
{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import nbformat as nbf\n",
    "import pandas as pd\n",
    "import scrapbook as sb\n",
    "\n",
    "import nbclient"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Background\n",
    "\n",
    "This notebook uses `nbclient` to read and execute an *empty* notebook.\n",
    "The empty notebook generates some fake data, makes a plot, and stores\n",
    "both the data and the plot inside the notebook using the\n",
    "[scrapbook package](https://github.com/nteract/scrapbook). We will\n",
    "then be able to access the generated contents of the notebook here.\n",
    "\n",
    "You can see the empty notebook by clicking this button:\n",
    "\n",
    "<a href=\"empty_notebook.ipynb\"><button>Empty notebook</button></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Read and execute the empty notebook"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# We use nbformat to represent our empty notebook in-memory\n",
    "nb = nbf.read(\"./empty_notebook.ipynb\", nbf.NO_CONVERT)\n",
    "\n",
    "# Execute our in-memory notebook, which will now have outputs\n",
    "nb = nbclient.execute(nb)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Inspect the new notebook for its contents"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# First we'll convert our nbformat NotebokNote into a *scrapbook* NotebookNode\n",
    "nb = sb.read_notebook(nb)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# We can access the dataframe that was created and glued into the empty notebook\n",
    "pd.DataFrame.from_dict(nb.scraps.get(\"dataframe\").data).head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# We can also access the generated plot by \"re-gluing\" the notebook here\n",
    "nb.reglue(\"plot\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  },
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "state": {},
    "version_major": 2,
    "version_minor": 0
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}


================================================
FILE: docs/Makefile
================================================
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line.
SPHINXOPTS    =
SPHINXBUILD   = sphinx-build
SPHINXPROJ    = nbclient
SOURCEDIR     = .
BUILDDIR      = _build

# Put it first so that "make" without argument is like "make help".
help:
	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)


================================================
FILE: docs/UPDATE.md
================================================
TODO: Figure out make options needed for non-api changes

```
sphinx-apidoc -f -o reference ../nbclient
```


================================================
FILE: docs/_static/custom.css
================================================
img.logo {
  width:100%
}

.right-next {
    float: right;
    max-width: 45%;
    overflow: auto;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.right-next::after{
    content: ' »';
}

.left-prev {
    float: left;
    max-width: 45%;
    overflow: auto;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.left-prev::before{
    content: '« ';
}

.prev-next-bottom {
  margin-top: 3em;
}

.prev-next-top {
  margin-bottom: 1em;
}


================================================
FILE: docs/autogen_config.py
================================================
#!/usr/bin/env python
"""
autogen_config.py

Create config_options.rst, a Sphinx documentation source file.
Documents the options that may be set in nbconvert's configuration file,
jupyter_nbconvert_config.py.

"""
import os
import os.path

from nbclient.cli import NbClientApp

header = """\

.. This is an automatically generated file.
.. do not modify by hand.

.. _other-full-config:

Config file and command line options
====================================
Jupyter ``nbclient`` can be run with a variety of command line arguments.
A list of available options can be found below in the :ref:`options section
<options>`.

.. _options:

Options
-------
This list of options can be generated by running the following and hitting
enter::

  $ jupyter execute --help-all

"""

try:
    indir = os.path.dirname(__file__)
except NameError:
    indir = os.getcwd()

destination = os.path.join(indir, "reference/config_options.rst")

with open(destination, "w") as f:
    app = NbClientApp()
    f.write(header)
    f.write(app.document_config_options())


================================================
FILE: docs/client.rst
================================================
Executing notebooks
===================

.. module:: nbclient.client.guide

Jupyter notebooks are often saved with output cells that have been cleared.
NBClient provides a convenient way to execute the input cells of an
.ipynb notebook file and save the results, both input and output cells,
as a .ipynb file.

In this section we show how to execute a ``.ipynb`` notebook
document saving the result in notebook format. If you need to export
notebooks to other formats, such as reStructured Text or Markdown (optionally
executing them) see `nbconvert <https://nbconvert.readthedocs.io/en/latest/>`_.

Executing notebooks can be very helpful, for example, to run all notebooks
in Python library in one step, or as a way to automate the data analysis in
projects involving more than one notebook.

Using the Python API interface
------------------------------

This section will illustrate the Python API interface.

Example
~~~~~~~

Let's start with a complete quick example, leaving detailed explanations
to the following sections.

**Import**: First we import nbformat and the :class:`NotebookClient`
class::

    import nbformat
    from nbclient import NotebookClient

**Load**: Assuming that ``notebook_filename`` contains the path to a notebook,
we can load it with::

    nb = nbformat.read(notebook_filename, as_version=4)

**Configure**: Next, we configure the notebook execution mode::

    client = NotebookClient(nb, timeout=600, kernel_name='python3', resources={'metadata': {'path': 'notebooks/'}})

We specified two (optional) arguments ``timeout`` and ``kernel_name``, which
define respectively the cell execution timeout and the execution kernel.
Usually you don't need to set these options, but these and other options are
available to control execution context. Note that ``path`` specifies
in which folder to execute the notebook.

**Execute/Run**: To actually run the notebook we call the method
``execute``::

    client.execute()

Hopefully, we will not get any errors during the notebook execution
(see the last section for error handling). This notebook will
now have its cell outputs populated with the result of running
each cell.

**Save**: Finally, save the resulting notebook with::

    nbformat.write(nb, 'executed_notebook.ipynb')

That's all. Your executed notebook will be saved in the current folder
in the file ``executed_notebook.ipynb``.

Execution arguments (traitlets)
-------------------------------

The arguments passed to :class:`NotebookClient` are configuration options
called `traitlets <https://traitlets.readthedocs.io/en/stable>`_.
There are many cool things about traitlets. For example,
they enforce the input type, and they can be accessed/modified as
class attributes.

Let's now discuss in more detail the two traitlets we used.

The ``timeout`` traitlet defines the maximum time (in seconds) each notebook
cell is allowed to run, if the execution takes longer an exception will be
raised. The default is 30 s, so in cases of long-running cells you may want to
specify an higher value. The ``timeout`` option can also be set to ``None``
or ``-1`` to remove any restriction on execution time.

The second traitlet, ``kernel_name``, allows specifying the name of the kernel
to be used for the execution. By default, the kernel name is obtained from the
notebook metadata. The traitlet ``kernel_name`` allows specifying a
user-defined kernel, overriding the value in the notebook metadata. A common
use case is that of a Python 2/3 library which includes documentation/testing
notebooks. These notebooks will specify either a python2 or python3 kernel in
their metadata (depending on the kernel used the last time the notebook was
saved). In reality, these notebooks will work on both Python 2 and Python 3,
and, for testing, it is important to be able to execute them programmatically
on both versions. Here the traitlet ``kernel_name`` helps simplify and
maintain consistency: we can just run a notebook twice, specifying first
"python2" and then "python3" as the kernel name.

Hooks before and after notebook or cell execution
-------------------------------------------------
There are several configurable hooks that allow the user to execute code before and
after a notebook or a cell is executed. Each one is configured with a function that will be called in its
respective place in the execution pipeline.
Each is described below:

**Notebook-level hooks**: These hooks are called with a single extra parameter:

- ``notebook=NotebookNode``: the current notebook being executed.

Here is the available hooks:

- ``on_notebook_start`` will run when the notebook client is initialized, before any execution has happened.
- ``on_notebook_complete`` will run when the notebook client has finished executing, after kernel cleanup.
- ``on_notebook_error`` will run when the notebook client has encountered an exception before kernel cleanup.

**Cell-level hooks**: These hooks are called with at least two parameters:

- ``cell=NotebookNode``: a reference to the current cell.
- ``cell_index=int``: the index of the cell in the current notebook's list of cells.

Here are the available hooks:

- ``on_cell_start`` will run for all cell types before the cell is executed.
- ``on_cell_execute`` will run right before the code cell is executed.
- ``on_cell_complete`` will run after execution, if the cell is executed with no errors.
- ``on_cell_executed`` will run right after the code cell is executed.
- ``on_cell_error`` will run if there is an error during cell execution.

``on_cell_executed`` and ``on_cell_error`` are called with an extra parameter ``execute_reply=dict``.


Handling errors and exceptions
------------------------------

In the previous sections we saw how to save an executed notebook, assuming
there are no execution errors. But, what if there are errors?

Execution until first error
~~~~~~~~~~~~~~~~~~~~~~~~~~~
An error during the notebook execution, by default, will stop the execution
and raise a ``CellExecutionError``. Conveniently, the source cell causing
the error and the original error name and message are also printed.
After an error, we can still save the notebook as before::

    nbformat.write(nb, 'executed_notebook.ipynb')

The saved notebook contains the output up until the failing cell,
and includes a full stack-trace and error (which can help debugging).

Handling errors
~~~~~~~~~~~~~~~
A useful pattern to execute notebooks while handling errors is the following::

    from nbclient.exceptions import CellExecutionError

    try:
        client.execute()
    except CellExecutionError:
        msg = 'Error executing the notebook "%s".\n\n' % notebook_filename
        msg += 'See notebook "%s" for the traceback.' % notebook_filename_out
        print(msg)
        raise
    finally:
        nbformat.write(nb, notebook_filename_out)

This will save the executed notebook regardless of execution errors.
In case of errors, however, an additional message is printed and the
``CellExecutionError`` is raised. The message directs the user to
the saved notebook for further inspection.

Execute and save all errors
~~~~~~~~~~~~~~~~~~~~~~~~~~~
As a last scenario, it is sometimes useful to execute notebooks which raise
exceptions, for example to show an error condition. In this case, instead of
stopping the execution on the first error, we can keep executing the notebook
using the traitlet ``allow_errors`` (default is False). With
``allow_errors=True``, the notebook is executed until the end, regardless of
any error encountered during the execution. The output notebook, will contain
the stack-traces and error messages for **all** the cells raising exceptions.

Widget state
------------

If your notebook contains any
`Jupyter Widgets <https://github.com/jupyter-widgets/ipywidgets/>`_,
the state of all the widgets can be stored in the notebook's metadata.
This allows rendering of the live widgets on for instance nbviewer, or when
converting to html.

We can tell nbclient to not store the state using the ``store_widget_state``
argument::

    client = NotebookClient(nb, store_widget_state=False)

This widget rendering is not performed against a browser during execution, so
only widget default states or states manipulated via user code will be
calculated during execution. ``%%javascript`` cells will execute upon notebook
rendering, enabling complex interactions to function as expected when viewed by
a UI.

If you can't view widget results after execution, you may need to select
:menuselection:`Trust Notebook` under the :menuselection:`File` menu.

Using a command-line interface
------------------------------

This section will illustrate how to run notebooks from your terminal. It supports the most basic use case. For more sophisticated execution options, consider the `papermill <https://pypi.org/project/papermill/>`_ library.

This library's command line tool is available by running ``jupyter execute``. It expects notebooks as input arguments and accepts optional flags to modify the default behavior.

Running a notebook is this easy.::

    jupyter execute notebook.ipynb

You can pass more than one notebook as well.::

    jupyter execute notebook.ipynb notebook2.ipynb

By default, notebook errors will be raised and printed into the terminal. You can suppress them by passing the ``--allow-errors`` flag.::

    jupyter execute notebook.ipynb --allow-errors

Other options allow you to modify the timeout length and dictate the kernel in use. A full set of options is available via the help command.::

    jupyter execute --help

    An application used to execute notebook files (*.ipynb)

    Options
    =======
    The options below are convenience aliases to configurable class-options,
    as listed in the "Equivalent to" description-line of the aliases.
    To see all configurable class-options for some <cmd>, use:
        <cmd> --help-all

    --allow-errors
        Errors are ignored and execution is continued until the end of the notebook.
        Equivalent to: [--NbClientApp.allow_errors=True]
    --timeout=<Int>
        The time to wait (in seconds) for output from executions. If a cell
        execution takes longer, a TimeoutError is raised. ``-1`` will disable the
        timeout.
        Default: None
        Equivalent to: [--NbClientApp.timeout]
    --startup_timeout=<Int>
        The time to wait (in seconds) for the kernel to start. If kernel startup
        takes longer, a RuntimeError is raised.
        Default: 60
        Equivalent to: [--NbClientApp.startup_timeout]
    --kernel_name=<Unicode>
        Name of kernel to use to execute the cells. If not set, use the kernel_spec
        embedded in the notebook.
        Default: ''
        Equivalent to: [--NbClientApp.kernel_name]

    To see all available configurables, use `--help-all`.


================================================
FILE: docs/conf.py
================================================
#!/usr/bin/env python3
#
# nbclient documentation build configuration file, created by
# sphinx-quickstart on Mon Jan 26 16:00:00 2020.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import shutil
import sys

import nbclient

sys.path.insert(0, os.path.abspath(".."))

# -- General configuration ------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    "sphinx.ext.autodoc",
    "sphinx.ext.intersphinx",
    "sphinx.ext.mathjax",
    "sphinx.ext.napoleon",
    # 'autodoc_traits',  # TODO
    "myst_parser",
]

try:
    import enchant  # type:ignore  # noqa

    extensions += ["sphinxcontrib.spelling"]
except ImportError:
    pass

autodoc_mock_imports = ["pytest", "nbconvert", "testpath"]

# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
source_suffix = [".rst", ".md"]

# The master toctree document.
master_doc = "index"

# General information about the project.
project = "nbclient"
copyright = "2020, Project Jupyter"  # noqa
author = "Project Jupyter"

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#

# The short X.Y version.
version = ".".join(nbclient.__version__.split(".")[0:2])

# The full version, including alpha/beta/rc tags.
release = nbclient.__version__

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line foexitr these cases.
language = "en"

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "UPDATE.md"]

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = "sphinx"

# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False

default_role = "any"

# -- Options for HTML output ----------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
#
html_theme = "sphinx_book_theme"

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#

html_theme_options = {
    "path_to_docs": "docs",
    "repository_url": "https://github.com/jupyter/nbclient",
    "repository_branch": "master",
    "use_edit_page_button": True,
    "navigation_with_keys": False,
}

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]

# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
# html_sidebars = {}

html_title = "nbclient"

# -- Options for HTMLHelp output ------------------------------------------

# Output file base name for HTML help builder.
htmlhelp_basename = "nclientdoc"


# -- Options for LaTeX output ---------------------------------------------

# latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
# }

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, documentclass [howto, manual, or own class]).
latex_documents = [(master_doc, "nbclient.tex", "nbclient Documentation", "jupyter team", "manual")]


# -- Options for manual page output ---------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [(master_doc, "nbclient", "nbclient Documentation", [author], 1)]


# -- Options for Texinfo output -------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
    (
        master_doc,
        "nbclient",
        "nbclient Documentation",
        author,
        "nbclient",
        "One line description of project.",
        "Miscellaneous",
    )
]

# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {"python": ("https://docs.python.org/", None)}


def setup(app):
    here = os.path.abspath(os.path.dirname(__file__))
    dest = os.path.join(here, "changelog.md")
    shutil.copy(os.path.join(here, "..", "CHANGELOG.md"), dest)

    autogen_config = os.path.join(here, "autogen_config.py")
    prev_dir = os.getcwd()
    os.chdir(here)
    with open(autogen_config) as f:
        exec(compile(f.read(), autogen_config, "exec"), {})  # noqa
    os.chdir(prev_dir)


================================================
FILE: docs/index.rst
================================================
Welcome to nbclient
===================

.. image:: https://img.shields.io/github/stars/jupyter/nbclient?label=stars&style=social
   :alt: GitHub stars
   :target: https://github.com/jupyter/nbclient
.. image:: https://github.com/jupyter/nbclient/workflows/CI/badge.svg
   :alt: GitHub Actions
   :target: https://github.com/jupyter/nbclient/actions
.. image:: https://codecov.io/github/jupyter/nbclient/coverage.svg?branch=master
   :alt: CodeCov
   :target: https://codecov.io/gh/jupyter/nbclient

---

**NBClient** lets you **execute** notebooks.

A client library for programmatic notebook execution, **NBClient** is a tool for running Jupyter Notebooks in
different execution contexts, including the command line. NBClient was spun out of `nbconvert <https://nbconvert.readthedocs.io/en/latest/>`_'s
former ``ExecutePreprocessor``.

Demo
----

To demo **NBClient** interactively, click the Binder link below:

.. image:: https://mybinder.org/badge_logo.svg
    :target: https://mybinder.org/v2/gh/jupyter/nbclient/master?filepath=binder%2Frun_nbclient.ipynb

Origins
-------

This library used to be part of `nbconvert <https://nbconvert.readthedocs.io/en/latest/>`_ and was extracted into its ownlibrary for easier updating and importing by downstream libraries and applications.

Python Version Support
----------------------

This library currently supports python 3.6+ versions. As minor python
versions are officially sunset by the python org, nbclient will similarly
drop support in the future.

Documentation
-------------

These pages guide you through the installation and usage of nbclient.

.. toctree::
   :maxdepth: 1
   :caption: Documentation

   installation
   client
   changelog


API Reference
-------------

If you are looking for information about a specific function, class, or method,
this documentation section will help you.

.. toctree::
   :maxdepth: 3
   :caption: Table of Contents

   reference/index.rst

Indices and tables
------------------

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`


================================================
FILE: docs/installation.rst
================================================
Installation
============

Installing nbclient
-------------------

From the command line:

.. code-block:: bash

   python3 -m pip install nbclient


.. seealso::

   `Installing Jupyter <https://jupyter.readthedocs.io/en/latest/install.html>`__
     NBClient is part of the Jupyter ecosystem.


================================================
FILE: docs/make.bat
================================================
@ECHO OFF

pushd %~dp0

REM Command file for Sphinx documentation

if "%SPHINXBUILD%" == "" (
	set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
set SPHINXPROJ=nbclient

if "%1" == "" goto help

%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
	echo.
	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
	echo.installed, then set the SPHINXBUILD environment variable to point
	echo.to the full path of the 'sphinx-build' executable. Alternatively you
	echo.may add the Sphinx directory to PATH.
	echo.
	echo.If you don't have Sphinx installed, grab it from
	echo.http://sphinx-doc.org/
	exit /b 1
)

%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
goto end

:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%

:end
popd


================================================
FILE: docs/reference/index.rst
================================================
Reference
=========

This part of the documentation lists the full API reference of all public classes and functions.

.. toctree::
   :maxdepth: 2

   nbclient
   config_options
   modules


================================================
FILE: docs/reference/modules.rst
================================================
nbclient
========

.. toctree::
   :maxdepth: 4

   nbclient


================================================
FILE: docs/reference/nbclient.rst
================================================
nbclient package
================

Subpackages
-----------

Submodules
----------

nbclient.client module
----------------------

.. automodule:: nbclient.client
   :members:
   :undoc-members:
   :show-inheritance:

nbclient.exceptions module
--------------------------

.. automodule:: nbclient.exceptions
   :members:
   :undoc-members:
   :show-inheritance:


Module contents
---------------

.. automodule:: nbclient
   :members:
   :undoc-members:
   :show-inheritance:


================================================
FILE: nbclient/__init__.py
================================================
from ._version import __version__, version_info
from .client import NotebookClient, execute

__all__ = ["__version__", "version_info", "NotebookClient", "execute"]


================================================
FILE: nbclient/_version.py
================================================
"""Version info."""
from __future__ import annotations

import re

__version__ = "0.10.4"

# Build up version_info tuple for backwards compatibility
pattern = r"(?P<major>\d+).(?P<minor>\d+).(?P<patch>\d+)(?P<rest>.*)"
match = re.match(pattern, __version__)
if match:
    parts: list[int | str] = [int(match[part]) for part in ["major", "minor", "patch"]]
    if match["rest"]:
        parts.append(match["rest"])
else:
    parts = []
version_info = tuple(parts)


================================================
FILE: nbclient/cli.py
================================================
"""nbclient cli."""
from __future__ import annotations

import logging
import sys
import typing
from pathlib import Path
from textwrap import dedent

import nbformat
from jupyter_core.application import JupyterApp
from traitlets import Bool, Integer, List, Unicode, default
from traitlets.config import catch_config_error

from nbclient import __version__

from .client import NotebookClient

# mypy: disable-error-code="no-untyped-call"

nbclient_aliases: dict[str, str] = {
    "timeout": "NbClientApp.timeout",
    "startup_timeout": "NbClientApp.startup_timeout",
    "kernel_name": "NbClientApp.kernel_name",
    "output": "NbClientApp.output_base",
}

nbclient_flags: dict[str, typing.Any] = {
    "allow-errors": (
        {
            "NbClientApp": {
                "allow_errors": True,
            },
        },
        "Errors are ignored and execution is continued until the end of the notebook.",
    ),
    "inplace": (
        {
            "NbClientApp": {
                "inplace": True,
            },
        },
        "Overwrite input notebook with executed results.",
    ),
}


class NbClientApp(JupyterApp):
    """
    An application used to execute notebook files (``*.ipynb``)
    """

    version = Unicode(__version__)
    name = "jupyter-execute"
    aliases = nbclient_aliases
    flags = nbclient_flags

    description = "An application used to execute notebook files (*.ipynb)"
    notebooks = List(Unicode(), help="Path of notebooks to convert").tag(config=True)
    timeout = Integer(
        None,
        allow_none=True,
        help=dedent(
            """
            The time to wait (in seconds) for output from executions.
            If a cell execution takes longer, a TimeoutError is raised.
            ``-1`` will disable the timeout.
            """
        ),
    ).tag(config=True)
    startup_timeout = Integer(
        60,
        help=dedent(
            """
            The time to wait (in seconds) for the kernel to start.
            If kernel startup takes longer, a RuntimeError is
            raised.
            """
        ),
    ).tag(config=True)
    allow_errors = Bool(
        False,
        help=dedent(
            """
            When a cell raises an error the default behavior is that
            execution is stopped and a :py:class:`nbclient.exceptions.CellExecutionError`
            is raised.
            If this flag is provided, errors are ignored and execution
            is continued until the end of the notebook.
            """
        ),
    ).tag(config=True)
    skip_cells_with_tag = Unicode(
        "skip-execution",
        help=dedent(
            """
            Name of the cell tag to use to denote a cell that should be skipped.
            """
        ),
    ).tag(config=True)
    kernel_name = Unicode(
        "",
        help=dedent(
            """
            Name of kernel to use to execute the cells.
            If not set, use the kernel_spec embedded in the notebook.
            """
        ),
    ).tag(config=True)
    inplace = Bool(
        False,
        help=dedent(
            """
            Default is execute notebook without writing the newly executed notebook.
            If this flag is provided, the newly generated notebook will
            overwrite the input notebook.
            """
        ),
    ).tag(config=True)
    output_base = Unicode(
        None,
        allow_none=True,
        help=dedent(
            """
            Write executed notebook to this file base name.
            Supports pattern replacements ``'{notebook_name}'``,
            the name of the input notebook file without extension.
            Note that output is always relative to the parent directory of the
            input notebook.
            """
        ),
    ).tag(config=True)

    @default("log_level")
    def _log_level_default(self) -> int:
        return logging.INFO

    @catch_config_error
    def initialize(self, argv: list[str] | None = None) -> None:
        """Initialize the app."""
        super().initialize(argv)

        # Get notebooks to run
        self.notebooks = self.get_notebooks()

        # If there are none, throw an error
        if not self.notebooks:
            sys.exit(-1)

        # If output, must have single notebook
        if len(self.notebooks) > 1 and self.output_base is not None:
            if "{notebook_name}" not in self.output_base:
                msg = (
                    "If passing multiple notebooks with `--output=output` option, "
                    "output string must contain {notebook_name}"
                )
                raise ValueError(msg)

        # Loop and run them one by one
        for path in self.notebooks:
            self.run_notebook(path)

    def get_notebooks(self) -> list[str]:
        """Get the notebooks for the app."""
        # If notebooks were provided from the command line, use those
        if self.extra_args:
            notebooks = self.extra_args
        # If not, look to the class attribute
        else:
            notebooks = self.notebooks

        # Return what we got.
        return notebooks

    def run_notebook(self, notebook_path: str) -> None:
        """Run a notebook by path."""
        # Log it
        self.log.info(f"Executing {notebook_path}")

        input_path = Path(notebook_path).with_suffix(".ipynb")

        # Get its parent directory so we can add it to the $PATH
        path = input_path.parent.absolute()

        # Optional output of executed notebook
        if self.inplace:
            output_path = input_path
        elif self.output_base:
            output_path = input_path.parent.joinpath(
                self.output_base.format(notebook_name=input_path.with_suffix("").name)
            ).with_suffix(".ipynb")
        else:
            output_path = None

        if output_path and not output_path.parent.is_dir():
            msg = f"Cannot write to directory={output_path.parent} that does not exist"
            raise ValueError(msg)

        # Open up the notebook we're going to run
        with input_path.open() as f:
            nb = nbformat.read(f, as_version=4)

        # Configure nbclient to run the notebook
        client = NotebookClient(
            nb,
            timeout=self.timeout,
            startup_timeout=self.startup_timeout,
            skip_cells_with_tag=self.skip_cells_with_tag,
            allow_errors=self.allow_errors,
            kernel_name=self.kernel_name,
            resources={"metadata": {"path": path}},
        )

        # Run it
        client.execute()

        # Save it
        if output_path:
            self.log.info(f"Save executed results to {output_path}")
            nbformat.write(nb, output_path)


main = NbClientApp.launch_instance


================================================
FILE: nbclient/client.py
================================================
"""nbclient implementation."""
from __future__ import annotations

import asyncio
import atexit
import base64
import collections
import datetime
import re
import signal
import typing as t
from contextlib import asynccontextmanager, contextmanager
from queue import Empty
from textwrap import dedent
from time import monotonic

from jupyter_client.client import KernelClient
from jupyter_client.manager import KernelManager
from nbformat import NotebookNode
from nbformat.v4 import output_from_msg
from traitlets import Any, Bool, Callable, Dict, Enum, Integer, List, Type, Unicode, default
from traitlets.config.configurable import LoggingConfigurable

from .exceptions import (
    CellControlSignal,
    CellExecutionComplete,
    CellExecutionError,
    CellTimeoutError,
    DeadKernelError,
)
from .output_widget import OutputWidget
from .util import ensure_async, run_hook, run_sync

_RGX_CARRIAGERETURN = re.compile(r".*\r(?=[^\n])")
_RGX_BACKSPACE = re.compile(r"[^\n]\b")

# mypy: disable-error-code="no-untyped-call"


def timestamp(msg: dict[str, t.Any] | None = None) -> str:
    """Get the timestamp for a message."""
    if msg and "header" in msg:  # The test mocks don't provide a header, so tolerate that
        msg_header = msg["header"]
        if "date" in msg_header and isinstance(msg_header["date"], datetime.datetime):
            try:
                # reformat datetime into expected format
                formatted_time = datetime.datetime.strftime(
                    msg_header["date"], "%Y-%m-%dT%H:%M:%S.%fZ"
                )
                if (
                    formatted_time
                ):  # docs indicate strftime may return empty string, so let's catch that too
                    return formatted_time
            except Exception:  # noqa
                pass  # fallback to a local time

    return datetime.datetime.utcnow().isoformat() + "Z"


class NotebookClient(LoggingConfigurable):
    """
    Encompasses a Client for executing cells in a notebook
    """

    timeout = Integer(
        None,
        allow_none=True,
        help=dedent(
            """
            The time to wait (in seconds) for output from executions.
            If a cell execution takes longer, a TimeoutError is raised.

            ``None`` or ``-1`` will disable the timeout. If ``timeout_func`` is set,
            it overrides ``timeout``.
            """
        ),
    ).tag(config=True)

    timeout_func: t.Callable[..., int | None] | None = Any(  # type:ignore[assignment]
        default_value=None,
        allow_none=True,
        help=dedent(
            """
            A callable which, when given the cell source as input,
            returns the time to wait (in seconds) for output from cell
            executions. If a cell execution takes longer, a TimeoutError
            is raised.

            Returning ``None`` or ``-1`` will disable the timeout for the cell.
            Not setting ``timeout_func`` will cause the client to
            default to using the ``timeout`` trait for all cells. The
            ``timeout_func`` trait overrides ``timeout`` if it is not ``None``.
            """
        ),
    ).tag(config=True)

    interrupt_on_timeout = Bool(
        False,
        help=dedent(
            """
            If execution of a cell times out, interrupt the kernel and
            continue executing other cells rather than throwing an error and
            stopping.
            """
        ),
    ).tag(config=True)

    error_on_timeout = Dict(
        default_value=None,
        allow_none=True,
        help=dedent(
            """
            If a cell execution was interrupted after a timeout, don't wait for
            the execute_reply from the kernel (e.g. KeyboardInterrupt error).
            Instead, return an execute_reply with the given error, which should
            be of the following form::

                {
                    'ename': str,  # Exception name, as a string
                    'evalue': str,  # Exception value, as a string
                    'traceback': list(str),  # traceback frames, as strings
                }
            """
        ),
    ).tag(config=True)

    startup_timeout = Integer(
        60,
        help=dedent(
            """
            The time to wait (in seconds) for the kernel to start.
            If kernel startup takes longer, a RuntimeError is
            raised.
            """
        ),
    ).tag(config=True)

    allow_errors = Bool(
        False,
        help=dedent(
            """
            If ``False`` (default), when a cell raises an error the
            execution is stopped and a ``CellExecutionError``
            is raised, except if the error name is in
            ``allow_error_names``.
            If ``True``, execution errors are ignored and the execution
            is continued until the end of the notebook. Output from
            exceptions is included in the cell output in both cases.
            """
        ),
    ).tag(config=True)

    allow_error_names = List(
        Unicode(),
        help=dedent(
            """
            List of error names which won't stop the execution. Use this if the
            ``allow_errors`` option it too general and you want to allow only
            specific kinds of errors.
            """
        ),
    ).tag(config=True)

    force_raise_errors = Bool(
        False,
        help=dedent(
            """
            If False (default), errors from executing the notebook can be
            allowed with a ``raises-exception`` tag on a single cell, or the
            ``allow_errors`` or ``allow_error_names`` configurable options for
            all cells. An allowed error will be recorded in notebook output, and
            execution will continue. If an error occurs when it is not
            explicitly allowed, a ``CellExecutionError`` will be raised.
            If True, ``CellExecutionError`` will be raised for any error that occurs
            while executing the notebook. This overrides the ``allow_errors``
            and ``allow_error_names`` options and the ``raises-exception`` cell
            tag.
            """
        ),
    ).tag(config=True)

    skip_cells_with_tag = Unicode(
        "skip-execution",
        help=dedent(
            """
            Name of the cell tag to use to denote a cell that should be skipped.
            """
        ),
    ).tag(config=True)

    extra_arguments = List(Unicode()).tag(config=True)

    kernel_name = Unicode(
        "",
        help=dedent(
            """
            Name of kernel to use to execute the cells.
            If not set, use the kernel_spec embedded in the notebook.
            """
        ),
    ).tag(config=True)

    raise_on_iopub_timeout = Bool(
        False,
        help=dedent(
            """
            If ``False`` (default), then the kernel will continue waiting for
            iopub messages until it receives a kernel idle message, or until a
            timeout occurs, at which point the currently executing cell will be
            skipped. If ``True``, then an error will be raised after the first
            timeout. This option generally does not need to be used, but may be
            useful in contexts where there is the possibility of executing
            notebooks with memory-consuming infinite loops.
            """
        ),
    ).tag(config=True)

    store_widget_state = Bool(
        True,
        help=dedent(
            """
            If ``True`` (default), then the state of the Jupyter widgets created
            at the kernel will be stored in the metadata of the notebook.
            """
        ),
    ).tag(config=True)

    record_timing = Bool(
        True,
        help=dedent(
            """
            If ``True`` (default), then the execution timings of each cell will
            be stored in the metadata of the notebook.
            """
        ),
    ).tag(config=True)

    iopub_timeout = Integer(
        4,
        allow_none=False,
        help=dedent(
            """
            The time to wait (in seconds) for IOPub output. This generally
            doesn't need to be set, but on some slow networks (such as CI
            systems) the default timeout might not be long enough to get all
            messages.
            """
        ),
    ).tag(config=True)

    shell_timeout_interval = Integer(
        5,
        allow_none=False,
        help=dedent(
            """
            The time to wait (in seconds) for Shell output before retrying.
            This generally doesn't need to be set, but if one needs to check
            for dead kernels at a faster rate this can help.
            """
        ),
    ).tag(config=True)

    shutdown_kernel = Enum(
        ["graceful", "immediate"],
        default_value="graceful",
        help=dedent(
            """
            If ``graceful`` (default), then the kernel is given time to clean
            up after executing all cells, e.g., to execute its ``atexit`` hooks.
            If ``immediate``, then the kernel is signaled to immediately
            terminate.
            """
        ),
    ).tag(config=True)

    ipython_hist_file = Unicode(
        default_value=":memory:",
        help="""Path to file to use for SQLite history database for an IPython kernel.

        The specific value ``:memory:`` (including the colon
        at both end but not the back ticks), avoids creating a history file. Otherwise, IPython
        will create a history file for each kernel.

        When running kernels simultaneously (e.g. via multiprocessing) saving history a single
        SQLite file can result in database errors, so using ``:memory:`` is recommended in
        non-interactive contexts.
        """,
    ).tag(config=True)

    kernel_manager_class = Type(
        config=True, klass=KernelManager, help="The kernel manager class to use."
    )

    on_notebook_start = Callable(
        default_value=None,
        allow_none=True,
        help=dedent(
            """
            A callable which executes after the kernel manager and kernel client are setup, and
            cells are about to execute.
            Called with kwargs ``notebook``.
            """
        ),
    ).tag(config=True)

    on_notebook_complete = Callable(
        default_value=None,
        allow_none=True,
        help=dedent(
            """
            A callable which executes after the kernel is cleaned up.
            Called with kwargs ``notebook``.
            """
        ),
    ).tag(config=True)

    on_notebook_error = Callable(
        default_value=None,
        allow_none=True,
        help=dedent(
            """
            A callable which executes when the notebook encounters an error.
            Called with kwargs ``notebook``.
            """
        ),
    ).tag(config=True)

    on_cell_start = Callable(
        default_value=None,
        allow_none=True,
        help=dedent(
            """
            A callable which executes before a cell is executed and before non-executing cells
            are skipped.
            Called with kwargs ``cell`` and ``cell_index``.
            """
        ),
    ).tag(config=True)

    on_cell_execute = Callable(
        default_value=None,
        allow_none=True,
        help=dedent(
            """
            A callable which executes just before a code cell is executed.
            Called with kwargs ``cell`` and ``cell_index``.
            """
        ),
    ).tag(config=True)

    on_cell_complete = Callable(
        default_value=None,
        allow_none=True,
        help=dedent(
            """
            A callable which executes after a cell execution is complete. It is
            called even when a cell results in a failure.
            Called with kwargs ``cell`` and ``cell_index``.
            """
        ),
    ).tag(config=True)

    on_cell_executed = Callable(
        default_value=None,
        allow_none=True,
        help=dedent(
            """
            A callable which executes just after a code cell is executed, whether
            or not it results in an error.
            Called with kwargs ``cell``, ``cell_index`` and ``execute_reply``.
            """
        ),
    ).tag(config=True)

    on_cell_error = Callable(
        default_value=None,
        allow_none=True,
        help=dedent(
            """
            A callable which executes when a cell execution results in an error.
            This is executed even if errors are suppressed with ``cell_allows_errors``.
            Called with kwargs ``cell`, ``cell_index`` and ``execute_reply``.
            """
        ),
    ).tag(config=True)

    @default("kernel_manager_class")
    def _kernel_manager_class_default(self) -> type[KernelManager]:
        """Use a dynamic default to avoid importing jupyter_client at startup"""
        from jupyter_client import AsyncKernelManager  # type:ignore[attr-defined]

        return AsyncKernelManager

    _display_id_map: dict[str, t.Any] = Dict(  # type:ignore[assignment]
        help=dedent(
            """
              mapping of locations of outputs with a given display_id
              tracks cell index and output index within cell.outputs for
              each appearance of the display_id
              {
                   'display_id': {
                  cell_idx: [output_idx,]
                   }
              }
              """
        )
    )

    display_data_priority = List(
        [
            "text/html",
            "application/pdf",
            "text/latex",
            "image/svg+xml",
            "image/png",
            "image/jpeg",
            "text/markdown",
            "text/plain",
        ],
        help="""
            An ordered list of preferred output type, the first
            encountered will usually be used when converting discarding
            the others.
            """,
    ).tag(config=True)

    resources: dict[str, t.Any] = Dict(  # type:ignore[assignment]
        help=dedent(
            """
            Additional resources used in the conversion process. For example,
            passing ``{'metadata': {'path': run_path}}`` sets the
            execution path to ``run_path``.
            """
        )
    )

    coalesce_streams = Bool(
        help=dedent(
            """
            Merge all stream outputs with shared names into single streams.
            """
        )
    )

    def __init__(self, nb: NotebookNode, km: KernelManager | None = None, **kw: t.Any) -> None:
        """Initializes the execution manager.

        Parameters
        ----------
        nb : NotebookNode
            Notebook being executed.
        km : KernelManager (optional)
            Optional kernel manager. If none is provided, a kernel manager will
            be created.
        """
        super().__init__(**kw)
        self.nb: NotebookNode = nb
        self.km: KernelManager | None = km
        self.owns_km: bool = km is None  # whether the NotebookClient owns the kernel manager
        self.kc: KernelClient | None = None
        self.reset_execution_trackers()
        self.widget_registry: dict[str, dict[str, t.Any]] = {
            "@jupyter-widgets/output": {"OutputModel": OutputWidget}
        }
        # comm_open_handlers should return an object with a .handle_msg(msg) method or None
        self.comm_open_handlers: dict[str, t.Any] = {
            "jupyter.widget": self.on_comm_open_jupyter_widget
        }

    def reset_execution_trackers(self) -> None:
        """Resets any per-execution trackers."""
        self.task_poll_for_reply: asyncio.Future[t.Any] | None = None
        self.code_cells_executed = 0
        self._display_id_map = {}
        self.widget_state: dict[str, dict[str, t.Any]] = {}
        self.widget_buffers: dict[str, dict[tuple[str, ...], dict[str, str]]] = {}
        # maps to list of hooks, where the last is used, this is used
        # to support nested use of output widgets.
        self.output_hook_stack: dict[str, list[OutputWidget]] = collections.defaultdict(list)
        # our front-end mimicking Output widgets
        self.comm_objects: dict[str, t.Any] = {}

    def create_kernel_manager(self) -> KernelManager:
        """Creates a new kernel manager.

        Returns
        -------
        km : KernelManager
            Kernel manager whose client class is asynchronous.
        """
        if not self.kernel_name:
            kn = self.nb.metadata.get("kernelspec", {}).get("name")
            if kn is not None:
                self.kernel_name = kn

        if not self.kernel_name:
            self.km = self.kernel_manager_class(config=self.config)
        else:
            self.km = self.kernel_manager_class(kernel_name=self.kernel_name, config=self.config)
        assert self.km is not None
        return self.km

    async def _async_cleanup_kernel(self) -> None:
        assert self.km is not None
        now = self.shutdown_kernel == "immediate"
        try:
            # Queue the manager to kill the process, and recover gracefully if it's already dead.
            if await ensure_async(self.km.is_alive()):
                await ensure_async(self.km.shutdown_kernel(now=now))
        except RuntimeError as e:
            # The error isn't specialized, so we have to check the message
            if "No kernel is running!" not in str(e):
                raise
        finally:
            # Remove any state left over even if we failed to stop the kernel
            await ensure_async(self.km.cleanup_resources())
            if getattr(self, "kc", None) and self.kc is not None:
                await ensure_async(self.kc.stop_channels())  # type:ignore[func-returns-value]
                self.kc = None
                self.km = None

    _cleanup_kernel = run_sync(_async_cleanup_kernel)

    async def async_start_new_kernel(self, **kwargs: t.Any) -> None:
        """Creates a new kernel.

        Parameters
        ----------
        kwargs :
            Any options for ``self.kernel_manager_class.start_kernel()``. Because
            that defaults to AsyncKernelManager, this will likely include options
            accepted by ``AsyncKernelManager.start_kernel()``, which includes ``cwd``.
        """
        assert self.km is not None
        resource_path = self.resources.get("metadata", {}).get("path") or None
        if resource_path and "cwd" not in kwargs:
            kwargs["cwd"] = resource_path

        has_history_manager_arg = any(
            arg.startswith("--HistoryManager.hist_file") for arg in self.extra_arguments
        )
        if (
            hasattr(self.km, "ipykernel")
            and self.km.ipykernel
            and self.ipython_hist_file
            and not has_history_manager_arg
        ):
            self.extra_arguments += [f"--HistoryManager.hist_file={self.ipython_hist_file}"]

        await ensure_async(self.km.start_kernel(extra_arguments=self.extra_arguments, **kwargs))

    start_new_kernel = run_sync(async_start_new_kernel)

    async def async_start_new_kernel_client(self) -> KernelClient:
        """Creates a new kernel client.

        Returns
        -------
        kc : KernelClient
            Kernel client as created by the kernel manager ``km``.
        """
        assert self.km is not None
        try:
            self.kc = self.km.client()
            await ensure_async(self.kc.start_channels())  # type:ignore[func-returns-value]
            await ensure_async(self.kc.wait_for_ready(timeout=self.startup_timeout))
        except Exception as e:
            self.log.error(
                "Error occurred while starting new kernel client for kernel {}: {}".format(
                    getattr(self.km, "kernel_id", None), str(e)
                )
            )
            await self._async_cleanup_kernel()
            raise
        self.kc.allow_stdin = False
        await run_hook(self.on_notebook_start, notebook=self.nb)
        return self.kc

    start_new_kernel_client = run_sync(async_start_new_kernel_client)

    @contextmanager
    def setup_kernel(self, **kwargs: t.Any) -> t.Generator[None, None, None]:
        """
        Context manager for setting up the kernel to execute a notebook.

        The assigns the Kernel Manager (``self.km``) if missing and Kernel Client(``self.kc``).

        When control returns from the yield it stops the client's zmq channels, and shuts
        down the kernel.
        """
        # by default, cleanup the kernel client if we own the kernel manager
        # and keep it alive if we don't
        cleanup_kc = kwargs.pop("cleanup_kc", self.owns_km)

        # Can't use run_until_complete on an asynccontextmanager function :(
        if self.km is None:
            self.km = self.create_kernel_manager()

        if not self.km.has_kernel:
            self.start_new_kernel(**kwargs)

        if self.kc is None:
            self.start_new_kernel_client()

        try:
            yield
        finally:
            if cleanup_kc:
                self._cleanup_kernel()

    @asynccontextmanager
    async def async_setup_kernel(self, **kwargs: t.Any) -> t.AsyncGenerator[None, None]:
        """
        Context manager for setting up the kernel to execute a notebook.

        This assigns the Kernel Manager (``self.km``) if missing and Kernel Client(``self.kc``).

        When control returns from the yield it stops the client's zmq channels, and shuts
        down the kernel.

        Handlers for SIGINT and SIGTERM are also added to cleanup in case of unexpected shutdown.
        """
        # by default, cleanup the kernel client if we own the kernel manager
        # and keep it alive if we don't
        cleanup_kc = kwargs.pop("cleanup_kc", self.owns_km)
        if self.km is None:
            self.km = self.create_kernel_manager()

        # self._cleanup_kernel uses run_async, which ensures the ioloop is running again.
        # This is necessary as the ioloop has stopped once atexit fires.
        atexit.register(self._cleanup_kernel)

        def on_signal() -> None:
            """Handle signals."""
            self._async_cleanup_kernel_future = asyncio.ensure_future(self._async_cleanup_kernel())
            atexit.unregister(self._cleanup_kernel)

        loop = asyncio.get_event_loop()
        try:
            loop.add_signal_handler(signal.SIGINT, on_signal)
            loop.add_signal_handler(signal.SIGTERM, on_signal)
        except RuntimeError:
            # NotImplementedError: Windows does not support signals.
            # RuntimeError: Raised when add_signal_handler is called outside the main thread
            pass

        if not self.km.has_kernel:
            await self.async_start_new_kernel(**kwargs)

        if self.kc is None:
            await self.async_start_new_kernel_client()

        try:
            yield
        except RuntimeError as e:
            await run_hook(self.on_notebook_error, notebook=self.nb)
            raise e
        finally:
            if cleanup_kc:
                await self._async_cleanup_kernel()
            await run_hook(self.on_notebook_complete, notebook=self.nb)
            atexit.unregister(self._cleanup_kernel)
            try:
                loop.remove_signal_handler(signal.SIGINT)
                loop.remove_signal_handler(signal.SIGTERM)
            except RuntimeError:
                pass

    async def async_execute(self, reset_kc: bool = False, **kwargs: t.Any) -> NotebookNode:
        """
        Executes each code cell.

        Parameters
        ----------
        kwargs :
            Any option for ``self.kernel_manager_class.start_kernel()``. Because
            that defaults to AsyncKernelManager, this will likely include options
            accepted by ``jupyter_client.AsyncKernelManager.start_kernel()``,
            which includes ``cwd``.

            ``reset_kc`` if True, the kernel client will be reset and a new one
            will be created (default: False).

        Returns
        -------
        nb : NotebookNode
            The executed notebook.
        """
        if reset_kc and self.owns_km:
            await self._async_cleanup_kernel()
        self.reset_execution_trackers()

        async with self.async_setup_kernel(**kwargs):
            assert self.kc is not None
            self.log.info("Executing notebook with kernel: %s" % self.kernel_name)
            msg_id = await ensure_async(self.kc.kernel_info())
            info_msg = await self.async_wait_for_reply(msg_id)
            if info_msg is not None:
                if "language_info" in info_msg["content"]:
                    self.nb.metadata["language_info"] = info_msg["content"]["language_info"]
                else:
                    raise RuntimeError(
                        'Kernel info received message content has no "language_info" key. '
                        "Content is:\n" + str(info_msg["content"])
                    )
            for index, cell in enumerate(self.nb.cells):
                # Ignore `'execution_count' in content` as it's always 1
                # when store_history is False
                await self.async_execute_cell(
                    cell, index, execution_count=self.code_cells_executed + 1
                )
            self.set_widgets_metadata()

        return self.nb

    execute = run_sync(async_execute)

    def set_widgets_metadata(self) -> None:
        """Set with widget metadata."""
        if self.widget_state:
            self.nb.metadata.widgets = {
                "application/vnd.jupyter.widget-state+json": {
                    "state": {
                        model_id: self._serialize_widget_state(state)
                        for model_id, state in self.widget_state.items()
                        if "_model_name" in state
                    },
                    "version_major": 2,
                    "version_minor": 0,
                }
            }
            for key, widget in self.nb.metadata.widgets[
                "application/vnd.jupyter.widget-state+json"
            ]["state"].items():
                buffers = self.widget_buffers.get(key)
                if buffers:
                    widget["buffers"] = list(buffers.values())

    def _update_display_id(self, display_id: str, msg: dict[str, t.Any]) -> None:
        """Update outputs with a given display_id"""
        if display_id not in self._display_id_map:
            self.log.debug("display id %r not in %s", display_id, self._display_id_map)
            return

        if msg["header"]["msg_type"] == "update_display_data":
            msg["header"]["msg_type"] = "display_data"

        try:
            out = output_from_msg(msg)
        except ValueError:
            self.log.error(f"unhandled iopub msg: {msg['msg_type']}")
            return

        for cell_idx, output_indices in self._display_id_map[display_id].items():
            cell = self.nb["cells"][cell_idx]
            outputs = cell["outputs"]
            for output_idx in output_indices:
                outputs[output_idx]["data"] = out["data"]
                outputs[output_idx]["metadata"] = out["metadata"]

    async def _async_poll_for_reply(
        self,
        msg_id: str,
        cell: NotebookNode,
        timeout: int | None,
        task_poll_output_msg: asyncio.Future[t.Any],
        task_poll_kernel_alive: asyncio.Future[t.Any],
    ) -> dict[str, t.Any]:
        msg: dict[str, t.Any]
        assert self.kc is not None
        new_timeout: float | None = None
        if timeout is not None:
            deadline = monotonic() + timeout
            new_timeout = float(timeout)
        error_on_timeout_execute_reply = None
        while True:
            try:
                if error_on_timeout_execute_reply:
                    msg = error_on_timeout_execute_reply
                    msg["parent_header"] = {"msg_id": msg_id}
                else:
                    msg = await ensure_async(self.kc.shell_channel.get_msg(timeout=new_timeout))
                if msg["parent_header"].get("msg_id") == msg_id:
                    if self.record_timing:
                        cell["metadata"]["execution"]["shell.execute_reply"] = timestamp(msg)
                    try:
                        await asyncio.wait_for(task_poll_output_msg, self.iopub_timeout)
                    except (asyncio.TimeoutError, Empty):
                        if self.raise_on_iopub_timeout:
                            task_poll_kernel_alive.cancel()
                            raise CellTimeoutError.error_from_timeout_and_cell(
                                "Timeout waiting for IOPub output", self.iopub_timeout, cell
                            ) from None
                        else:
                            self.log.warning("Timeout waiting for IOPub output")
                    task_poll_kernel_alive.cancel()
                    return msg
                else:
                    if new_timeout is not None:
                        new_timeout = max(0, deadline - monotonic())
            except Empty:
                # received no message, check if kernel is still alive
                assert timeout is not None
                task_poll_kernel_alive.cancel()
                await self._async_check_alive()
                error_on_timeout_execute_reply = await self._async_handle_timeout(timeout, cell)

    async def _async_poll_output_msg(
        self, parent_msg_id: str, cell: NotebookNode, cell_index: int
    ) -> None:
        assert self.kc is not None
        while True:
            msg = await ensure_async(self.kc.iopub_channel.get_msg(timeout=None))
            if msg["parent_header"].get("msg_id") == parent_msg_id:
                try:
                    # Will raise CellExecutionComplete when completed
                    self.process_message(msg, cell, cell_index)
                except CellExecutionComplete:
                    return

    async def _async_poll_kernel_alive(self) -> None:
        while True:
            await asyncio.sleep(1)
            try:
                await self._async_check_alive()
            except DeadKernelError:
                assert self.task_poll_for_reply is not None
                self.task_poll_for_reply.cancel()
                return

    def _get_timeout(self, cell: NotebookNode | None) -> int | None:
        if self.timeout_func is not None and cell is not None:
            timeout = self.timeout_func(cell)
        else:
            timeout = self.timeout

        if not timeout or timeout < 0:
            timeout = None

        return timeout

    async def _async_handle_timeout(
        self, timeout: int, cell: NotebookNode | None = None
    ) -> None | dict[str, t.Any]:
        self.log.error("Timeout waiting for execute reply (%is)." % timeout)
        if self.interrupt_on_timeout:
            self.log.error("Interrupting kernel")
            assert self.km is not None
            await ensure_async(self.km.interrupt_kernel())
            if self.error_on_timeout:
                execute_reply = {"content": {**self.error_on_timeout, "status": "error"}}
                return execute_reply
            return None
        else:
            assert cell is not None
            raise CellTimeoutError.error_from_timeout_and_cell(
                "Cell execution timed out", timeout, cell
            )

    async def _async_check_alive(self) -> None:
        assert self.kc is not None
        if not await ensure_async(self.kc.is_alive()):  # type:ignore[attr-defined]
            self.log.error("Kernel died while waiting for execute reply.")
            raise DeadKernelError("Kernel died")

    async def async_wait_for_reply(
        self, msg_id: str, cell: NotebookNode | None = None
    ) -> dict[str, t.Any] | None:
        """Wait for a message reply."""
        assert self.kc is not None
        # wait for finish, with timeout
        timeout = self._get_timeout(cell)
        cummulative_time = 0
        while True:
            try:
                msg: dict[str, t.Any] = await ensure_async(
                    self.kc.shell_channel.get_msg(timeout=self.shell_timeout_interval)
                )
            except Empty:
                await self._async_check_alive()
                cummulative_time += self.shell_timeout_interval
                if timeout and cummulative_time > timeout:
                    await self._async_handle_timeout(timeout, cell)
                    break
            else:
                if msg["parent_header"].get("msg_id") == msg_id:
                    return msg
        return None

    wait_for_reply = run_sync(async_wait_for_reply)
    # Backwards compatibility naming for papermill
    _wait_for_reply = wait_for_reply

    def _passed_deadline(self, deadline: int | None) -> bool:
        if deadline is not None and deadline - monotonic() <= 0:
            return True
        return False

    async def _check_raise_for_error(
        self, cell: NotebookNode, cell_index: int, exec_reply: dict[str, t.Any] | None
    ) -> None:
        if exec_reply is None:
            return None

        exec_reply_content = exec_reply["content"]
        if exec_reply_content["status"] != "error":
            return None

        cell_allows_errors = (not self.force_raise_errors) and (
            self.allow_errors
            or exec_reply_content.get("ename") in self.allow_error_names
            or "raises-exception" in cell.metadata.get("tags", [])
        )
        await run_hook(
            self.on_cell_error, cell=cell, cell_index=cell_index, execute_reply=exec_reply
        )
        if not cell_allows_errors:
            raise CellExecutionError.from_cell_and_msg(cell, exec_reply_content)

    async def async_execute_cell(
        self,
        cell: NotebookNode,
        cell_index: int,
        execution_count: int | None = None,
        store_history: bool = True,
    ) -> NotebookNode:
        """
        Executes a single code cell.

        To execute all cells see :meth:`execute`.

        Parameters
        ----------
        cell : nbformat.NotebookNode
            The cell which is currently being processed.
        cell_index : int
            The position of the cell within the notebook object.
        execution_count : int
            The execution count to be assigned to the cell (default: Use kernel response)
        store_history : bool
            Determines if history should be stored in the kernel (default: False).
            Specific to ipython kernels, which can store command histories.

        Returns
        -------
        output : dict
            The execution output payload (or None for no output).

        Raises
        ------
        CellExecutionError
            If execution failed and should raise an exception, this will be raised
            with defaults about the failure.

        Returns
        -------
        cell : NotebookNode
            The cell which was just processed.
        """
        assert self.kc is not None

        await run_hook(self.on_cell_start, cell=cell, cell_index=cell_index)

        if cell.cell_type != "code" or not cell.source.strip():
            self.log.debug("Skipping non-executing cell %s", cell_index)
            return cell

        if self.skip_cells_with_tag in cell.metadata.get("tags", []):
            self.log.debug("Skipping tagged cell %s", cell_index)
            return cell

        if self.record_timing:  # clear execution metadata prior to execution
            cell["metadata"]["execution"] = {}

        self.log.debug("Executing cell:\n%s", cell.source)

        cell_allows_errors = (not self.force_raise_errors) and (
            self.allow_errors or "raises-exception" in cell.metadata.get("tags", [])
        )

        await run_hook(self.on_cell_execute, cell=cell, cell_index=cell_index)
        parent_msg_id = await ensure_async(
            self.kc.execute(
                cell.source, store_history=store_history, stop_on_error=not cell_allows_errors
            )
        )
        await run_hook(self.on_cell_complete, cell=cell, cell_index=cell_index)
        # We launched a code cell to execute
        self.code_cells_executed += 1
        exec_timeout = self._get_timeout(cell)

        cell.outputs = []
        self.clear_before_next_output = False

        task_poll_kernel_alive = asyncio.ensure_future(self._async_poll_kernel_alive())
        task_poll_output_msg = asyncio.ensure_future(
            self._async_poll_output_msg(parent_msg_id, cell, cell_index)
        )
        self.task_poll_for_reply = asyncio.ensure_future(
            self._async_poll_for_reply(
                parent_msg_id, cell, exec_timeout, task_poll_output_msg, task_poll_kernel_alive
            )
        )
        try:
            exec_reply = await self.task_poll_for_reply
        except asyncio.CancelledError:
            # can only be cancelled by task_poll_kernel_alive when the kernel is dead
            task_poll_output_msg.cancel()
            raise DeadKernelError("Kernel died") from None
        except Exception as e:
            # Best effort to cancel request if it hasn't been resolved
            try:
                # Check if the task_poll_output is doing the raising for us
                if not isinstance(e, CellControlSignal):
                    task_poll_output_msg.cancel()
            finally:
                raise

        if execution_count:
            cell["execution_count"] = execution_count
        await run_hook(
            self.on_cell_executed, cell=cell, cell_index=cell_index, execute_reply=exec_reply
        )

        if self.coalesce_streams and cell.outputs:
            new_outputs = []
            streams: dict[str, NotebookNode] = {}
            for output in cell.outputs:
                if output["output_type"] == "stream":
                    if output["name"] in streams:
                        streams[output["name"]]["text"] += output["text"]
                    else:
                        new_outputs.append(output)
                        streams[output["name"]] = output
                else:
                    new_outputs.append(output)

            # process \r and \b characters
            for output in streams.values():
                old = output["text"]
                while len(output["text"]) < len(old):
                    old = output["text"]
                    # Cancel out anything-but-newline followed by backspace
                    output["text"] = _RGX_BACKSPACE.sub("", output["text"])
                # Replace all carriage returns not followed by newline
                output["text"] = _RGX_CARRIAGERETURN.sub("", output["text"])

            # We also want to ensure stdout and stderr are always in the same consecutive order,
            # because they are asynchronous, so order isn't guaranteed.
            for i, output in enumerate(new_outputs):
                if output["output_type"] == "stream" and output["name"] == "stderr":
                    if (
                        len(new_outputs) >= i + 2
                        and new_outputs[i + 1]["output_type"] == "stream"
                        and new_outputs[i + 1]["name"] == "stdout"
                    ):
                        stdout = new_outputs.pop(i + 1)
                        new_outputs.insert(i, stdout)

            cell.outputs = new_outputs

        await self._check_raise_for_error(cell, cell_index, exec_reply)

        self.nb["cells"][cell_index] = cell
        return cell

    execute_cell = run_sync(async_execute_cell)

    def process_message(
        self, msg: dict[str, t.Any], cell: NotebookNode, cell_index: int
    ) -> NotebookNode | None:
        """
        Processes a kernel message, updates cell state, and returns the
        resulting output object that was appended to cell.outputs.

        The input argument *cell* is modified in-place.

        Parameters
        ----------
        msg : dict
            The kernel message being processed.
        cell : nbformat.NotebookNode
            The cell which is currently being processed.
        cell_index : int
            The position of the cell within the notebook object.

        Returns
        -------
        output : NotebookNode
            The execution output payload (or None for no output).

        Raises
        ------
        CellExecutionComplete
          Once a message arrives which indicates computation completeness.

        """
        msg_type = msg["msg_type"]
        self.log.debug("msg_type: %s", msg_type)
        content = msg["content"]
        self.log.debug("content: %s", content)

        # while it's tempting to go for a more concise
        # display_id = content.get("transient", {}).get("display_id", None)
        # this breaks if transient is explicitly set to None
        transient = content.get("transient")
        display_id = transient.get("display_id") if transient else None

        if display_id and msg_type in {"execute_result", "display_data", "update_display_data"}:
            self._update_display_id(display_id, msg)

        # set the prompt number for the input and the output
        if "execution_count" in content:
            cell["execution_count"] = content["execution_count"]

        if self.record_timing:
            if msg_type == "status":
                if content["execution_state"] == "idle":
                    cell["metadata"]["execution"]["iopub.status.idle"] = timestamp(msg)
                elif content["execution_state"] == "busy":
                    cell["metadata"]["execution"]["iopub.status.busy"] = timestamp(msg)
            elif msg_type == "execute_input":
                cell["metadata"]["execution"]["iopub.execute_input"] = timestamp(msg)

        if msg_type == "status":
            if content["execution_state"] == "idle":
                raise CellExecutionComplete()
        elif msg_type == "clear_output":
            self.clear_output(cell.outputs, msg, cell_index)
        elif msg_type.startswith("comm"):
            self.handle_comm_msg(cell.outputs, msg, cell_index)
        # Check for remaining messages we don't process
        elif msg_type not in ["execute_input", "update_display_data"]:
            # Assign output as our processed "result"
            return self.output(cell.outputs, msg, display_id, cell_index)
        return None

    def output(
        self,
        outs: list[NotebookNode],
        msg: dict[str, t.Any],
        display_id: str | None,
        cell_index: int,
    ) -> NotebookNode | None:
        """Handle output."""

        msg_type = msg["msg_type"]
        out: NotebookNode | None = None

        parent_msg_id = msg["parent_header"].get("msg_id")
        if self.output_hook_stack[parent_msg_id]:
            # if we have a hook registered, it will override our
            # default output behaviour (e.g. OutputWidget)
            hook = self.output_hook_stack[parent_msg_id][-1]
            hook.output(outs, msg, display_id, cell_index)
            return None

        try:
            out = output_from_msg(msg)
        except ValueError:
            self.log.error(f"unhandled iopub msg: {msg_type}")
            return None

        if self.clear_before_next_output:
            self.log.debug("Executing delayed clear_output")
            outs[:] = []
            self.clear_display_id_mapping(cell_index)
            self.clear_before_next_output = False

        if display_id:
            # record output index in:
            #   _display_id_map[display_id][cell_idx]
            cell_map = self._display_id_map.setdefault(display_id, {})
            output_idx_list = cell_map.setdefault(cell_index, [])
            output_idx_list.append(len(outs))

        if out:
            outs.append(out)

        return out

    def clear_output(
        self, outs: list[NotebookNode], msg: dict[str, t.Any], cell_index: int
    ) -> None:
        """Clear output."""
        content = msg["content"]

        parent_msg_id = msg["parent_header"].get("msg_id")
        if self.output_hook_stack[parent_msg_id]:
            # if we have a hook registered, it will override our
            # default clear_output behaviour (e.g. OutputWidget)
            hook = self.output_hook_stack[parent_msg_id][-1]
            hook.clear_output(outs, msg, cell_index)
            return

        if content.get("wait"):
            self.log.debug("Wait to clear output")
            self.clear_before_next_output = True
        else:
            self.log.debug("Immediate clear output")
            outs[:] = []
            self.clear_display_id_mapping(cell_index)

    def clear_display_id_mapping(self, cell_index: int) -> None:
        """Clear a display id mapping for a cell."""
        for _, cell_map in self._display_id_map.items():
            if cell_index in cell_map:
                cell_map[cell_index] = []

    def handle_comm_msg(
        self, outs: list[NotebookNode], msg: dict[str, t.Any], cell_index: int
    ) -> None:
        """Handle a comm message."""
        content = msg["content"]
        data = content["data"]
        if self.store_widget_state and "state" in data:  # ignore custom msg'es
            self.widget_state.setdefault(content["comm_id"], {}).update(data["state"])
            if data.get("buffer_paths"):
                comm_id = content["comm_id"]
                if comm_id not in self.widget_buffers:
                    self.widget_buffers[comm_id] = {}
                # for each comm, the path uniquely identifies a buffer
                new_buffers: dict[tuple[str, ...], dict[str, str]] = {
                    tuple(k["path"]): k for k in self._get_buffer_data(msg)
                }
                self.widget_buffers[comm_id].update(new_buffers)
        # There are cases where we need to mimic a frontend, to get similar behaviour as
        # when using the Output widget from Jupyter lab/notebook
        if msg["msg_type"] == "comm_open":
            target = msg["content"].get("target_name")
            handler = self.comm_open_handlers.get(target)
            if handler:
                comm_id = msg["content"]["comm_id"]
                comm_object = handler(msg)
                if comm_object:
                    self.comm_objects[comm_id] = comm_object
            else:
                self.log.warning(f"No handler found for comm target {target!r}")
        elif msg["msg_type"] == "comm_msg":
            content = msg["content"]
            comm_id = msg["content"]["comm_id"]
            if comm_id in self.comm_objects:
                self.comm_objects[comm_id].handle_msg(msg)

    def _serialize_widget_state(self, state: dict[str, t.Any]) -> dict[str, t.Any]:
        """Serialize a widget state, following format in @jupyter-widgets/schema."""
        return {
            "model_name": state.get("_model_name"),
            "model_module": state.get("_model_module"),
            "model_module_version": state.get("_model_module_version"),
            "state": state,
        }

    def _get_buffer_data(self, msg: dict[str, t.Any]) -> list[dict[str, str]]:
        encoded_buffers = []
        paths = msg["content"]["data"]["buffer_paths"]
        buffers = msg["buffers"]
        for path, buffer in zip(paths, buffers, strict=False):
            encoded_buffers.append(
                {
                    "data": base64.b64encode(buffer).decode("utf-8"),
                    "encoding": "base64",
                    "path": path,
                }
            )
        return encoded_buffers

    def register_output_hook(self, msg_id: str, hook: OutputWidget) -> None:
        """Registers an override object that handles output/clear_output instead.

        Multiple hooks can be registered, where the last one will be used (stack based)
        """
        # mimics
        # https://jupyterlab.github.io/jupyterlab/services/interfaces/kernel.ikernelconnection.html#registermessagehook
        self.output_hook_stack[msg_id].append(hook)

    def remove_output_hook(self, msg_id: str, hook: OutputWidget) -> None:
        """Unregisters an override object that handles output/clear_output instead"""
        # mimics
        # https://jupyterlab.github.io/jupyterlab/services/interfaces/kernel.ikernelconnection.html#removemessagehook
        removed_hook = self.output_hook_stack[msg_id].pop()
        assert removed_hook == hook

    def on_comm_open_jupyter_widget(self, msg: dict[str, t.Any]) -> t.Any | None:
        """Handle a jupyter widget comm open."""
        content = msg["content"]
        data = content["data"]
        state = data["state"]
        comm_id = msg["content"]["comm_id"]
        module = self.widget_registry.get(state["_model_module"])
        if module:
            widget_class = module.get(state["_model_name"])
            if widget_class:
                return widget_class(comm_id, state, self.kc, self)
        return None


def execute(
    nb: NotebookNode,
    cwd: str | None = None,
    km: KernelManager | None = None,
    **kwargs: t.Any,
) -> NotebookNode:
    """Execute a notebook's code, updating outputs within the notebook object.

    This is a convenient wrapper around NotebookClient. It returns the
    modified notebook object.

    Parameters
    ----------
    nb : NotebookNode
      The notebook object to be executed
    cwd : str, optional
      If supplied, the kernel will run in this directory
    km : AsyncKernelManager, optional
      If supplied, the specified kernel manager will be used for code execution.
    kwargs :
      Any other options for NotebookClient, e.g. timeout, kernel_name
    """
    resources = {}
    if cwd is not None:
        resources["metadata"] = {"path": cwd}
    return NotebookClient(nb=nb, resources=resources, km=km, **kwargs).execute()


================================================
FILE: nbclient/exceptions.py
================================================
"""Exceptions for nbclient."""
from __future__ import annotations

from typing import Any

from nbformat import NotebookNode


class CellControlSignal(Exception):  # noqa
    """
    A custom exception used to indicate that the exception is used for cell
    control actions (not the best model, but it's needed to cover existing
    behavior without major refactors).
    """

    pass


class CellTimeoutError(TimeoutError, CellControlSignal):
    """
    A custom exception to capture when a cell has timed out during execution.
    """

    @classmethod
    def error_from_timeout_and_cell(
        cls, msg: str, timeout: int, cell: NotebookNode
    ) -> CellTimeoutError:
        """Create an error from a timeout on a cell."""
        if cell and cell.source:
            src_by_lines = cell.source.strip().split("\n")
            src = (
                cell.source
                if len(src_by_lines) < 11
                else f"{src_by_lines[:5]}\n...\n{src_by_lines[-5:]}"
            )
        else:
            src = "Cell contents not found."
        return cls(timeout_err_msg.format(timeout=timeout, msg=msg, cell_contents=src))


class DeadKernelError(RuntimeError):
    """A dead kernel error."""

    pass


class CellExecutionComplete(CellControlSignal):
    """
    Used as a control signal for cell execution across execute_cell and
    process_message function calls. Raised when all execution requests
    are completed and no further messages are expected from the kernel
    over zeromq channels.
    """

    pass


class CellExecutionError(CellControlSignal):
    """
    Custom exception to propagate exceptions that are raised during
    notebook execution to the caller. This is mostly useful when
    using nbconvert as a library, since it allows to deal with
    failures gracefully.
    """

    def __init__(self, traceback: str, ename: str, evalue: str) -> None:
        """Initialize the error."""
        super().__init__(traceback)
        self.traceback = traceback
        self.ename = ename
        self.evalue = evalue

    def __reduce__(self) -> tuple[Any]:
        """Reduce implementation."""
        return type(self), (self.traceback, self.ename, self.evalue)  # type:ignore[return-value]

    def __str__(self) -> str:
        """Str repr."""
        if self.traceback:
            return self.traceback
        else:
            return f"{self.ename}: {self.evalue}"

    @classmethod
    def from_cell_and_msg(cls, cell: NotebookNode, msg: dict[str, Any]) -> CellExecutionError:
        """Instantiate from a code cell object and a message contents
        (message is either execute_reply or error)
        """

        # collect stream outputs for our error message
        stream_outputs: list[str] = []
        for output in cell.outputs:
            if output["output_type"] == "stream":
                stream_outputs.append(
                    stream_output_msg.format(name=output["name"], text=output["text"].rstrip())
                )
        if stream_outputs:
            # add blank line before, trailing separator
            # if there is any stream output to display
            stream_outputs.insert(0, "")
            stream_outputs.append("------------------")
        stream_output: str = "\n".join(stream_outputs)

        tb = "\n".join(msg.get("traceback", []) or [])
        return cls(
            exec_err_msg.format(
                cell=cell,
                stream_output=stream_output,
                traceback=tb,
            ),
            ename=msg.get("ename", "<Error>"),
            evalue=msg.get("evalue", ""),
        )


stream_output_msg: str = """\
----- {name} -----
{text}"""

exec_err_msg: str = """\
An error occurred while executing the following cell:
------------------
{cell.source}
------------------
{stream_output}

{traceback}
"""


timeout_err_msg: str = """\
A cell timed out while it was being executed, after {timeout} seconds.
The message was: {msg}.
Here is a preview of the cell contents:
-------------------
{cell_contents}
-------------------
"""


================================================
FILE: nbclient/jsonutil.py
================================================
"""Utilities to manipulate JSON objects."""

# NOTE: this is a copy of ipykernel/jsonutils.py (+blackified)

# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
from __future__ import annotations

import math
import numbers
import re
import types
from binascii import b2a_base64
from datetime import datetime
from typing import Any

# -----------------------------------------------------------------------------
# Globals and constants
# -----------------------------------------------------------------------------

# timestamp formats
ISO8601 = "%Y-%m-%dT%H:%M:%S.%f"
ISO8601_PAT = re.compile(
    r"^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(\.\d{1,6})?Z?([\+\-]\d{2}:?\d{2})?$"
)

# holy crap, strptime is not threadsafe.
# Calling it once at import seems to help.
datetime.strptime("2000-01-01", "%Y-%m-%d")

# -----------------------------------------------------------------------------
# Classes and functions
# -----------------------------------------------------------------------------


# constants for identifying png/jpeg data
PNG = b"\x89PNG\r\n\x1a\n"
# front of PNG base64-encoded
PNG64 = b"iVBORw0KG"
JPEG = b"\xff\xd8"
# front of JPEG base64-encoded
JPEG64 = b"/9"
# constants for identifying gif data
GIF_64 = b"R0lGODdh"
GIF89_64 = b"R0lGODlh"
# front of PDF base64-encoded
PDF64 = b"JVBER"


def encode_images(format_dict: dict[str, str]) -> dict[str, str]:
    """b64-encodes images in a displaypub format dict

    Perhaps this should be handled in json_clean itself?

    Parameters
    ----------

    format_dict : dict
        A dictionary of display data keyed by mime-type

    Returns
    -------

    format_dict : dict
        A copy of the same dictionary,
        but binary image data ('image/png', 'image/jpeg' or 'application/pdf')
        is base64-encoded.

    """
    return format_dict


def json_clean(obj: Any) -> Any:
    """Clean an object to ensure it's safe to encode in JSON.

    Atomic, immutable objects are returned unmodified.  Sets and tuples are
    converted to lists, lists are copied and dicts are also copied.

    Note: dicts whose keys could cause collisions upon encoding (such as a dict
    with both the number 1 and the string '1' as keys) will cause a ValueError
    to be raised.

    Parameters
    ----------
    obj : any python object

    Returns
    -------
    out : object

      A version of the input which will not cause an encoding error when
      encoded as JSON.  Note that this function does not *encode* its inputs,
      it simply sanitizes it so that there will be no encoding errors later.

    """
    # types that are 'atomic' and ok in json as-is.
    atomic_ok = (str, type(None))

    # containers that we need to convert into lists
    container_to_list = (tuple, set, types.GeneratorType)

    # Since bools are a subtype of Integrals, which are a subtype of Reals,
    # we have to check them in that order.

    if isinstance(obj, bool):
        return obj

    if isinstance(obj, numbers.Integral):
        # cast int to int, in case subclasses override __str__ (e.g. boost enum, #4598)
        return int(obj)

    if isinstance(obj, numbers.Real):
        # cast out-of-range floats to their reprs
        if math.isnan(obj) or math.isinf(obj):
            return repr(obj)
        return float(obj)

    if isinstance(obj, atomic_ok):
        return obj

    if isinstance(obj, bytes):
        return b2a_base64(obj).decode("ascii")

    if isinstance(obj, container_to_list) or (
        hasattr(obj, "__iter__") and hasattr(obj, "__next__")
    ):
        obj = list(obj)

    if isinstance(obj, list):
        return [json_clean(x) for x in obj]

    if isinstance(obj, dict):
        # First, validate that the dict won't lose data in conversion due to
        # key collisions after stringification.  This can happen with keys like
        # True and 'true' or 1 and '1', which collide in JSON.
        nkeys = len(obj)
        nkeys_collapsed = len(set(map(str, obj)))
        if nkeys != nkeys_collapsed:
            raise ValueError(
                "dict cannot be safely converted to JSON: "
                "key collision would lead to dropped values"
            )
        # If all OK, proceed by making the new dict that will be json-safe
        out = {}
        for k, v in iter(obj.items()):
            out[str(k)] = json_clean(v)
        return out
    if isinstance(obj, datetime):
        return obj.strftime(ISO8601)

    # we don't understand it, it's probably an unserializable object
    raise ValueError("Can't clean for JSON: %r" % obj)


================================================
FILE: nbclient/output_widget.py
================================================
"""An output widget mimic."""
from __future__ import annotations

from typing import Any

from jupyter_client.client import KernelClient
from nbformat import NotebookNode
from nbformat.v4 import output_from_msg

from .jsonutil import json_clean


class OutputWidget:
    """This class mimics a front end output widget"""

    def __init__(
        self, comm_id: str, state: dict[str, Any], kernel_client: KernelClient, executor: Any
    ) -> None:
        """Initialize the widget."""
        self.comm_id: str = comm_id
        self.state: dict[str, Any] = state
        self.kernel_client: KernelClient = kernel_client
        self.executor = executor
        self.topic: bytes = ("comm-%s" % self.comm_id).encode("ascii")
        self.outputs: list[NotebookNode] = self.state["outputs"]
        self.clear_before_next_output: bool = False

    def clear_output(self, outs: list[NotebookNode], msg: dict[str, Any], cell_index: int) -> None:
        """Clear output."""
        self.parent_header = msg["parent_header"]
        content = msg["content"]
        if content.get("wait"):
            self.clear_before_next_output = True
        else:
            self.outputs = []
            # sync back the state to the kernel
            self.sync_state()
            if hasattr(self.executor, "widget_state"):
                # sync the state to the nbconvert state as well, since that is used for testing
                self.executor.widget_state[self.comm_id]["outputs"] = self.outputs

    def sync_state(self) -> None:
        """Sync state."""
        state = {"outputs": self.outputs}
        msg = {"method": "update", "state": state, "buffer_paths": []}
        self.send(msg)

    def _publish_msg(
        self,
        msg_type: str,
        data: dict[str, Any] | None = None,
        metadata: dict[str, Any] | None = None,
        buffers: list[Any] | None = None,
        **keys: Any,
    ) -> None:
        """Helper for sending a comm message on IOPub"""
        data = {} if data is None else data
        metadata = {} if metadata is None else metadata
        content = json_clean(dict(data=data, comm_id=self.comm_id, **keys))
        msg = self.kernel_client.session.msg(
            msg_type, content=content, parent=self.parent_header, metadata=metadata
        )
        self.kernel_client.shell_channel.send(msg)

    def send(
        self,
        data: dict[str, Any] | None = None,
        metadata: dict[str, Any] | None = None,
        buffers: list[Any] | None = None,
    ) -> None:
        """Send a comm message."""
        self._publish_msg("comm_msg", data=data, metadata=metadata, buffers=buffers)

    def output(
        self, outs: list[NotebookNode], msg: dict[str, Any], display_id: str | None, cell_index: int
    ) -> None:
        """Handle output."""
        if self.clear_before_next_output:
            self.outputs = []
            self.clear_before_next_output = False
        self.parent_header = msg["parent_header"]
        output = output_from_msg(msg)  # type:ignore[no-untyped-call]

        if self.outputs:
            # try to coalesce/merge output text
            last_output = self.outputs[-1]
            if (
                last_output["output_type"] == "stream"
                and output["output_type"] == "stream"
                and last_output["name"] == output["name"]
            ):
                last_output["text"] += output["text"]
            else:
                self.outputs.append(output)
        else:
            self.outputs.append(output)
        self.sync_state()
        if hasattr(self.executor, "widget_state"):
            # sync the state to the nbconvert state as well, since that is used for testing
            self.executor.widget_state[self.comm_id]["outputs"] = self.outputs

    def set_state(self, state: dict[str, Any]) -> None:
        """Set the state."""
        if "msg_id" in state:
            msg_id = state.get("msg_id")
            if msg_id:
                self.executor.register_output_hook(msg_id, self)
                self.msg_id = msg_id
            else:
                self.executor.remove_output_hook(self.msg_id, self)
                self.msg_id = msg_id

    def handle_msg(self, msg: dict[str, Any]) -> None:
        """Handle a message."""
        content = msg["content"]
        comm_id = content["comm_id"]
        if comm_id != self.comm_id:
            raise AssertionError("Mismatched comm id")
        data = content["data"]
        if "state" in data:
            self.set_state(data["state"])


================================================
FILE: nbclient/py.typed
================================================


================================================
FILE: nbclient/util.py
================================================
"""General utility methods"""

# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from __future__ import annotations

import inspect
from collections.abc import Callable
from typing import Any

from jupyter_core.utils import ensure_async, run_sync

__all__ = ["ensure_async", "run_sync", "run_hook"]


async def run_hook(hook: Callable[..., Any] | None, **kwargs: Any) -> None:
    """Run a hook callback."""
    if hook is None:
        return
    res = hook(**kwargs)
    if inspect.isawaitable(res):
        await res


================================================
FILE: pyproject.toml
================================================
[build-system]
requires = [
    "hatchling>=1.10.0",
]
build-backend = "hatchling.build"

[project]
name = "nbclient"
dynamic = [
    "version",
]
description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor."
readme = "README.md"
license = { file = "LICENSE" }
requires-python = ">=3.10.0"
authors = [
    { name = "Jupyter Development Team", email = "jupyter@googlegroups.com" },
]
keywords = [
    "executor",
    "jupyter",
    "notebook",
    "pipeline",
]
classifiers = [
    "Intended Audience :: Developers",
    "Intended Audience :: Science/Research",
    "Intended Audience :: System Administrators",
    "License :: OSI Approved :: BSD License",
    "Programming Language :: Python",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "Programming Language :: Python :: 3.13",
    "Programming Language :: Python :: 3.14",
]
dependencies = [
    "jupyter_client>=6.1.12",
    "jupyter_core>=4.12,!=5.0.*",
    "nbformat>=5.1.3",
    "traitlets>=5.4",
]

[project.optional-dependencies]
test = [
    "flaky",
    "ipykernel>=6.19.3",
    "ipython",
    "ipywidgets",
    "nbconvert>=7.1.0",
    "pytest-asyncio >=1.3.0",
    "pytest-cov>=4.0",
    "pytest>=9.0.1,<10",
    "testpath",
    "xmltodict",
]
docs = [
    "autodoc-traits",
    "mock",
    "moto",
    "myst-parser",
    "sphinx-book-theme",
    "sphinxcontrib_spelling",
    "sphinx>=1.7",
    "nbclient[test]",
]
dev = [
    "pre-commit",
]

[project.scripts]
jupyter-execute = "nbclient.cli:main"

[project.urls]
Documentation = "https://nbclient.readthedocs.io"
Funding = "https://numfocus.org/"
Homepage = "https://jupyter.org"
Source = "https://github.com/jupyter/nbclient"
Tracker = "https://github.com/jupyter/nbclient/issues"

[tool.hatch.version]
path = "nbclient/_version.py"

[tool.hatch.build.targets.sdist]
include = [
    "/nbclient",
    "/tests"
]

[tool.hatch.envs.docs]
features = ["docs"]
[tool.hatch.envs.docs.scripts]
build = "make -C docs html SPHINXOPTS='-W'"

[tool.hatch.envs.test]
features = ["test"]
[tool.hatch.envs.test.scripts]
test = "python -m pytest -vv {args}"
nowarn = "test -W default {args}"

[tool.hatch.envs.cov]
features = ["test"]
dependencies = ["coverage[toml]", "pytest-cov"]
[tool.hatch.envs.cov.scripts]
test = "python -m pytest -vv --cov nbclient --cov-branch --cov-report term-missing:skip-covered {args}"
nowarn = "test -W default {args}"

[tool.hatch.envs.lint]
detached = true
dependencies = ["pre-commit"]
[tool.hatch.envs.lint.scripts]
build = [
    "pre-commit run --all-files ruff",
    "pre-commit run --all-files ruff-format"
]

[tool.hatch.envs.typing]
dependencies = [ "pre-commit"]
detached = true
[tool.hatch.envs.typing.scripts]
test = "pre-commit run --all-files --hook-stage manual mypy"

[tool.pytest.ini_options]
minversion = "6.0"
xfail_strict = true
log_cli_level = "info"
addopts = [
  "-ra", "--durations=10", "--color=yes", "--doctest-modules",
   "--showlocals", "--strict-markers", "--strict-config"
]
testpaths = ["tests"]
filterwarnings= [
  # Fail on warnings
  "error",
  "module:Jupyter is migrating its paths:DeprecationWarning",
  "module:unclosed <socket.socket:ResourceWarning",
  "module:There is no current event loop:DeprecationWarning",
  "module:unclosed event loop:ResourceWarning",
  "module:Unclosed socket <zmq:ResourceWarning",
  "module:zmq.eventloop.ioloop is deprecated:DeprecationWarning",
  "module:subprocess .* is still running:ResourceWarning",
  "module:Unclosed context <zmq:ResourceWarning",
  "module:datetime.datetime.utc:DeprecationWarning",
  "module:'asyncio.WindowsSelectorEventLoopPolicy' is deprecated and slated for removal in Python 3.16:DeprecationWarning",
  "module:'asyncio.set_event_loop_policy' is deprecated and slated for removal in Python 3.16:DeprecationWarning",
]

[tool.coverage.report]
exclude_lines = [
  "pragma: no cover",
  "def __repr__",
  "if self.debug:",
  "if settings.DEBUG",
  "raise AssertionError",
  "raise NotImplementedError",
  "if 0:",
  "if __name__ == .__main__.:",
  "class .*\bProtocol\\):",
"@(abc\\.)?abstractmethod",
]

[tool.coverage.run]
relative_files = true
source = ["nbclient"]

[tool.mypy]
files = "nbclient"
python_version = "3.10"
strict = true
enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"]
warn_unreachable = true

[[tool.mypy.overrides]]
module = [
    "async_generator.*",
    "testpath",
    "xmltodict",
]
ignore_missing_imports = true

[tool.ruff]
line-length = 100

[tool.ruff.lint]
select = [
  "A", "B", "C", "E", "F", "FBT", "I", "N", "Q", "RUF", "S", "T",
  "UP", "W", "YTT",
]
ignore = [
# Q000 Single quotes found but double quotes preferred
"Q000",
# FBT001 Boolean positional arg in function definition
"FBT001", "FBT002", "FBT003",
# C901 `async_setup_kernel` is too complex (12)
"C901",
]

[tool.ruff.lint.per-file-ignores]
# S101 Use of `assert` detected
"tests/*" = ["S101"]
"nbclient/client.py" = ["S101"]
"*.ipynb" = ["B", "E402", "T201", "F821", "A001", "E722", "S110", "RUF001"]

[tool.interrogate]
ignore-init-module=true
ignore-private=true
ignore-semiprivate=true
ignore-property-decorators=true
ignore-nested-functions=true
ignore-nested-classes=true
fail-under=100
exclude = ["tests", "docs"]

[tool.repo-review]
ignore = ["PY005", "PY007", "GH102"]


================================================
FILE: tests/__init__.py
================================================


================================================
FILE: tests/base.py
================================================
import unittest

from nbformat import v4 as nbformat

# mypy: disable-error-code="no-untyped-call,no-untyped-def"


class NBClientTestsBase(unittest.TestCase):
    def build_notebook(self, with_json_outputs=False):
        """Build a notebook in memory for use with NotebookClient tests"""

        outputs = [
            nbformat.new_output("stream", name="stdout", text="a"),
            nbformat.new_output("display_data", data={"text/plain": "b"}),
            nbformat.new_output("stream", name="stdout", text="c"),
            nbformat.new_output("stream", name="stdout", text="d"),
            nbformat.new_output("stream", name="stderr", text="e"),
            nbformat.new_output("stream", name="stderr", text="f"),
            nbformat.new_output("display_data", data={"image/png": "Zw=="}),  # g
            nbformat.new_output("display_data", data={"application/pdf": "aA=="}),  # h
        ]
        if with_json_outputs:
            outputs.extend(
                [
                    nbformat.new_output("display_data", data={"application/json": [1, 2, 3]}),  # j
                    nbformat.new_output(
                        "display_data", data={"application/json": {"a": 1, "c": {"b": 2}}}
                    ),  # k
                    nbformat.new_output("display_data", data={"application/json": "abc"}),  # l
                    nbformat.new_output("display_data", data={"application/json": 15.03}),  # m
                ]
            )

        cells = [
            nbformat.new_code_cell(source="$ e $", execution_count=1, outputs=outputs),
            nbformat.new_markdown_cell(source="$ e $"),
        ]

        return nbformat.new_notebook(cells=cells)

    def build_resources(self):
        """Build an empty resources dictionary."""
        return {"metadata": {}}

    @classmethod
    def merge_dicts(cls, *dict_args):
        # Because this is annoying to do inline
        outcome = {}
        for d in dict_args:
            outcome.update(d)
        return outcome


================================================
FILE: tests/conftest.py
================================================
import asyncio
import os

# This is important for ipykernel to show the same string
# instead of randomly generated file names in outputs.
# See: https://github.com/ipython/ipykernel/blob/360685c6/ipykernel/compiler.py#L50-L55
os.environ["IPYKERNEL_CELL_NAME"] = "<IPY-INPUT>"

if os.name == "nt" and hasattr(asyncio, "WindowsSelectorEventLoopPolicy"):
    asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())


================================================
FILE: tests/fake_kernelmanager.py
================================================
from jupyter_client.manager import AsyncKernelManager

# mypy: disable-error-code="no-untyped-call,no-untyped-def"


class FakeCustomKernelManager(AsyncKernelManager):
    expected_methods = {"__init__": 0, "client": 0, "start_kernel": 0}  # noqa

    def __init__(self, *args, **kwargs):
        self.log.info("FakeCustomKernelManager initialized")
        self.expected_methods["__init__"] += 1
        super().__init__(*args, **kwargs)

    async def start_kernel(self, *args, **kwargs):
        self.log.info("FakeCustomKernelManager started a kernel")
        self.expected_methods["start_kernel"] += 1
        return await super().start_kernel(*args, **kwargs)

    def client(self, *args, **kwargs):
        self.log.info("FakeCustomKernelManager created a client")
        self.expected_methods["client"] += 1
        return super().client(*args, **kwargs)


================================================
FILE: tests/files/Autokill.ipynb
================================================
{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import signal\n",
    "\n",
    "pid = os.getpid()\n",
    "os.kill(pid, signal.SIGTERM)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}


================================================
FILE: tests/files/Check History in Memory.ipynb
================================================
{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython import get_ipython"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "ip = get_ipython()\n",
    "assert ip.history_manager.hist_file == \":memory:\""
   ]
  }
 ],
 "metadata": {},
 "nbformat": 4,
 "nbformat_minor": 2
}


================================================
FILE: tests/files/Clear Output.ipynb
================================================
{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.display import clear_output"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "9\n"
     ]
    }
   ],
   "source": [
    "for i in range(10):\n",
    "    clear_output()\n",
    "    print(i)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"Hello world\")\n",
    "clear_output()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello world"
     ]
    }
   ],
   "source": [
    "print(\"Hello world\", end=\"\")\n",
    "clear_output(wait=True)  # no output after this"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "world"
     ]
    }
   ],
   "source": [
    "print(\"Hello\", end=\"\")\n",
    "clear_output(wait=True)  # here we have new output after wait=True\n",
    "print(\"world\", end=\"\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Hello world'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "handle0 = display(\"Hello world\", display_id=\"id0\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'world'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "handle1 = display(\"Hello\", display_id=\"id1\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "handle1.update(\"world\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "handle2 = display(\"Hello world\", display_id=\"id2\")\n",
    "clear_output()  # clears all output, also with display_ids"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Hello world'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "handle3 = display(\"Hello world\", display_id=\"id3\")\n",
    "clear_output(wait=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "world"
     ]
    }
   ],
   "source": [
    "handle4 = display(\"Hello\", display_id=\"id4\")\n",
    "clear_output(wait=True)\n",
    "print(\"world\", end=\"\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "handle4.update(\"Hello world\")  # it is cleared, so it should not show up in the above cell"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}


================================================
FILE: tests/files/Disable Stdin.ipynb
================================================
{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "try:\n",
    "    input = raw_input\n",
    "except:\n",
    "    pass\n",
    "\n",
    "name = input(\"name: \")"
   ]
  }
 ],
 "metadata": {},
 "nbformat": 4,
 "nbformat_minor": 0
}


================================================
FILE: tests/files/Empty Cell.ipynb
================================================
{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Test that executing skips over an empty cell."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Code 1'"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\"Code 1\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Code 2'"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\"Code 2\""
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}


================================================
FILE: tests/files/Error.ipynb
================================================
{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "d200673b",
   "metadata": {},
   "outputs": [
    {
     "ename": "ZeroDivisionError",
     "evalue": "division by zero",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mZeroDivisionError\u001b[0m                         Traceback (most recent call last)",
      "\u001b[0;32m/tmp/ipykernel_1277493/182040962.py\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m0\u001b[0m\u001b[0;34m/\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mZeroDivisionError\u001b[0m: division by zero"
     ]
    }
   ],
   "source": [
    "0 / 0"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}


================================================
FILE: tests/files/Factorials.ipynb
================================================
{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "i, j = 1, 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2\n",
      "3\n",
      "5\n",
      "8\n",
      "13\n",
      "21\n",
      "34\n",
      "55\n",
      "89\n",
      "144\n"
     ]
    }
   ],
   "source": [
    "for m in range(10):\n",
    "    i, j = j, i + j\n",
    "    print(j)"
   ]
  }
 ],
 "metadata": {},
 "nbformat": 4,
 "nbformat_minor": 0
}


================================================
FILE: tests/files/HelloWorld.ipynb
================================================
{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello World\n"
     ]
    }
   ],
   "source": [
    "print(\"Hello World\")"
   ]
  }
 ],
 "metadata": {},
 "nbformat": 4,
 "nbformat_minor": 0
}


================================================
FILE: tests/files/Inline Image.ipynb
================================================
{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "from IPython.display import Image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": [
       "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\n"
      ],
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Image(\"python.png\")"
   ]
  }
 ],
 "metadata": {},
 "nbformat": 4,
 "nbformat_minor": 0
}


================================================
FILE: tests/files/Interrupt.ipynb
================================================
{
    "cells": [
     {
      "cell_type": "code",
      "execution_count": 1,
      "metadata": {
       "collapsed": false
      },
      "outputs": [
       {
        "ename": "KeyboardInterrupt",
        "evalue": "",
        "output_type": "error",
        "traceback": [
         "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
         "\u001b[0;31mKeyboardInterrupt\u001b[0m                         Traceback (most recent call last)",
         "\u001b[0;32m<ipython-input-1-31d18a52bf41>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mwhile\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;32mcontinue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
         "\u001b[0;31mKeyboardInterrupt\u001b[0m: "
        ]
       }
      ],
      "source": [
       "while True: continue"
      ]
     },
     {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {
       "collapsed": false
      },
      "outputs": [
       {
        "name": "stdout",
        "output_type": "stream",
        "text": [
         "done\n"
        ]
       }
      ],
      "source": [
       "print(\"done\")"
      ]
     }
    ],
    "metadata": {},
    "nbformat": 4,
    "nbformat_minor": 0
   }


================================================
FILE: tests/files/JupyterWidgets.ipynb
================================================
{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "f46f26da84b54255bccc3a69d7eb08de",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Label(value='Hello World')"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import ipywidgets\n",
    "\n",
    "label = ipywidgets.Label(\"Hello World\")\n",
    "display(label)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# it should also handle custom msg'es\n",
    "label.send({\"msg\": \"Hello\"})"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  },
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "state": {
     "8273e8fe9d9941a4a63c062158e0a630": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.4.0",
      "model_name": "DescriptionStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "a72770a4f541425f8fe85833a3dc2a8e": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.4.0",
      "model_name": "LabelModel",
      "state": {
       "context_menu": null,
       "layout": "IPY_MODEL_dec20f599109458ca607b1df5959469b",
       "style": "IPY_MODEL_8273e8fe9d9941a4a63c062158e0a630",
       "value": "Hello World"
      }
     },
     "dec20f599109458ca607b1df5959469b": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.1.0",
      "mo
Download .txt
gitextract_xrhwfx_4/

├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── check-release.yml
│       ├── main.yml
│       ├── prep-release.yml
│       ├── publish-changelog.yml
│       └── publish-release.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .readthedocs.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── RELEASING.md
├── binder/
│   ├── empty_notebook.ipynb
│   ├── environment.yml
│   └── run_nbclient.ipynb
├── docs/
│   ├── Makefile
│   ├── UPDATE.md
│   ├── _static/
│   │   └── custom.css
│   ├── autogen_config.py
│   ├── client.rst
│   ├── conf.py
│   ├── index.rst
│   ├── installation.rst
│   ├── make.bat
│   └── reference/
│       ├── index.rst
│       ├── modules.rst
│       └── nbclient.rst
├── nbclient/
│   ├── __init__.py
│   ├── _version.py
│   ├── cli.py
│   ├── client.py
│   ├── exceptions.py
│   ├── jsonutil.py
│   ├── output_widget.py
│   ├── py.typed
│   └── util.py
├── pyproject.toml
└── tests/
    ├── __init__.py
    ├── base.py
    ├── conftest.py
    ├── fake_kernelmanager.py
    ├── files/
    │   ├── Autokill.ipynb
    │   ├── Check History in Memory.ipynb
    │   ├── Clear Output.ipynb
    │   ├── Disable Stdin.ipynb
    │   ├── Empty Cell.ipynb
    │   ├── Error.ipynb
    │   ├── Factorials.ipynb
    │   ├── HelloWorld.ipynb
    │   ├── Inline Image.ipynb
    │   ├── Interrupt.ipynb
    │   ├── JupyterWidgets.ipynb
    │   ├── Other Comms.ipynb
    │   ├── Output.ipynb
    │   ├── Parallel Execute A.ipynb
    │   ├── Parallel Execute B.ipynb
    │   ├── SVG.ipynb
    │   ├── Skip Exceptions with Cell Tags.ipynb
    │   ├── Skip Exceptions.ipynb
    │   ├── Skip Execution with Cell Tag.ipynb
    │   ├── Sleep1s.ipynb
    │   ├── Unicode.ipynb
    │   ├── UnicodePy3.ipynb
    │   └── update-display-id.ipynb
    ├── test_cli.py
    ├── test_client.py
    └── test_util.py
Download .txt
SYMBOL INDEX (175 symbols across 12 files)

FILE: docs/conf.py
  function setup (line 189) | def setup(app):

FILE: nbclient/cli.py
  class NbClientApp (line 48) | class NbClientApp(JupyterApp):
    method _log_level_default (line 135) | def _log_level_default(self) -> int:
    method initialize (line 139) | def initialize(self, argv: list[str] | None = None) -> None:
    method get_notebooks (line 163) | def get_notebooks(self) -> list[str]:
    method run_notebook (line 175) | def run_notebook(self, notebook_path: str) -> None:

FILE: nbclient/client.py
  function timestamp (line 40) | def timestamp(msg: dict[str, t.Any] | None = None) -> str:
  class NotebookClient (line 60) | class NotebookClient(LoggingConfigurable):
    method _kernel_manager_class_default (line 388) | def _kernel_manager_class_default(self) -> type[KernelManager]:
    method __init__ (line 445) | def __init__(self, nb: NotebookNode, km: KernelManager | None = None, ...
    method reset_execution_trackers (line 470) | def reset_execution_trackers(self) -> None:
    method create_kernel_manager (line 483) | def create_kernel_manager(self) -> KernelManager:
    method _async_cleanup_kernel (line 503) | async def _async_cleanup_kernel(self) -> None:
    method async_start_new_kernel (line 524) | async def async_start_new_kernel(self, **kwargs: t.Any) -> None:
    method async_start_new_kernel_client (line 554) | async def async_start_new_kernel_client(self) -> KernelClient:
    method setup_kernel (line 582) | def setup_kernel(self, **kwargs: t.Any) -> t.Generator[None, None, None]:
    method async_setup_kernel (line 612) | async def async_setup_kernel(self, **kwargs: t.Any) -> t.AsyncGenerato...
    method async_execute (line 669) | async def async_execute(self, reset_kc: bool = False, **kwargs: t.Any)...
    method set_widgets_metadata (line 718) | def set_widgets_metadata(self) -> None:
    method _update_display_id (line 739) | def _update_display_id(self, display_id: str, msg: dict[str, t.Any]) -...
    method _async_poll_for_reply (line 761) | async def _async_poll_for_reply(
    method _async_poll_output_msg (line 808) | async def _async_poll_output_msg(
    method _async_poll_kernel_alive (line 821) | async def _async_poll_kernel_alive(self) -> None:
    method _get_timeout (line 831) | def _get_timeout(self, cell: NotebookNode | None) -> int | None:
    method _async_handle_timeout (line 842) | async def _async_handle_timeout(
    method _async_check_alive (line 860) | async def _async_check_alive(self) -> None:
    method async_wait_for_reply (line 866) | async def async_wait_for_reply(
    method _passed_deadline (line 894) | def _passed_deadline(self, deadline: int | None) -> bool:
    method _check_raise_for_error (line 899) | async def _check_raise_for_error(
    method async_execute_cell (line 920) | async def async_execute_cell(
    method process_message (line 1069) | def process_message(
    method output (line 1138) | def output(
    method clear_output (line 1182) | def clear_output(
    method clear_display_id_mapping (line 1204) | def clear_display_id_mapping(self, cell_index: int) -> None:
    method handle_comm_msg (line 1210) | def handle_comm_msg(
    method _serialize_widget_state (line 1245) | def _serialize_widget_state(self, state: dict[str, t.Any]) -> dict[str...
    method _get_buffer_data (line 1254) | def _get_buffer_data(self, msg: dict[str, t.Any]) -> list[dict[str, st...
    method register_output_hook (line 1268) | def register_output_hook(self, msg_id: str, hook: OutputWidget) -> None:
    method remove_output_hook (line 1277) | def remove_output_hook(self, msg_id: str, hook: OutputWidget) -> None:
    method on_comm_open_jupyter_widget (line 1284) | def on_comm_open_jupyter_widget(self, msg: dict[str, t.Any]) -> t.Any ...
  function execute (line 1298) | def execute(

FILE: nbclient/exceptions.py
  class CellControlSignal (line 9) | class CellControlSignal(Exception):  # noqa
  class CellTimeoutError (line 19) | class CellTimeoutError(TimeoutError, CellControlSignal):
    method error_from_timeout_and_cell (line 25) | def error_from_timeout_and_cell(
  class DeadKernelError (line 41) | class DeadKernelError(RuntimeError):
  class CellExecutionComplete (line 47) | class CellExecutionComplete(CellControlSignal):
  class CellExecutionError (line 58) | class CellExecutionError(CellControlSignal):
    method __init__ (line 66) | def __init__(self, traceback: str, ename: str, evalue: str) -> None:
    method __reduce__ (line 73) | def __reduce__(self) -> tuple[Any]:
    method __str__ (line 77) | def __str__(self) -> str:
    method from_cell_and_msg (line 85) | def from_cell_and_msg(cls, cell: NotebookNode, msg: dict[str, Any]) ->...

FILE: nbclient/jsonutil.py
  function encode_images (line 50) | def encode_images(format_dict: dict[str, str]) -> dict[str, str]:
  function json_clean (line 73) | def json_clean(obj: Any) -> Any:

FILE: nbclient/output_widget.py
  class OutputWidget (line 13) | class OutputWidget:
    method __init__ (line 16) | def __init__(
    method clear_output (line 28) | def clear_output(self, outs: list[NotebookNode], msg: dict[str, Any], ...
    method sync_state (line 42) | def sync_state(self) -> None:
    method _publish_msg (line 48) | def _publish_msg(
    method send (line 65) | def send(
    method output (line 74) | def output(
    method set_state (line 102) | def set_state(self, state: dict[str, Any]) -> None:
    method handle_msg (line 113) | def handle_msg(self, msg: dict[str, Any]) -> None:

FILE: nbclient/util.py
  function run_hook (line 16) | async def run_hook(hook: Callable[..., Any] | None, **kwargs: Any) -> None:

FILE: tests/base.py
  class NBClientTestsBase (line 8) | class NBClientTestsBase(unittest.TestCase):
    method build_notebook (line 9) | def build_notebook(self, with_json_outputs=False):
    method build_resources (line 41) | def build_resources(self):
    method merge_dicts (line 46) | def merge_dicts(cls, *dict_args):

FILE: tests/fake_kernelmanager.py
  class FakeCustomKernelManager (line 6) | class FakeCustomKernelManager(AsyncKernelManager):
    method __init__ (line 9) | def __init__(self, *args, **kwargs):
    method start_kernel (line 14) | async def start_kernel(self, *args, **kwargs):
    method client (line 19) | def client(self, *args, **kwargs):

FILE: tests/test_cli.py
  function jupyterapp (line 19) | def jupyterapp():
  function client (line 25) | def client():
  function writer (line 31) | def writer():
  function reader (line 37) | def reader():
  function path_open (line 43) | def path_open():
  function test_mult (line 58) | def test_mult(input_names, relative, inplace, jupyterapp, client, reader...
  function test_output (line 103) | def test_output(input_names, relative, output_base, jupyterapp, client, ...
  function test_bad_output_dir (line 156) | def test_bad_output_dir(jupyterapp, client, reader, writer, path_open):
  function test_cli_simple (line 173) | def test_cli_simple():
  function test_no_notebooks (line 180) | def test_no_notebooks(jupyterapp):

FILE: tests/test_client.py
  function get_executor_with_hooks (line 66) | def get_executor_with_hooks(nb=None, executor=None, async_hooks=False):
  class AsyncMock (line 92) | class AsyncMock(Mock):
  function make_future (line 96) | def make_future(obj: Any) -> asyncio.Future[Any]:
  function normalize_base64 (line 107) | def normalize_base64(b64_text):
  function run_notebook (line 116) | def run_notebook(filename, opts, resources=None):
  function run_notebook_wrapper (line 144) | def run_notebook_wrapper(args):
  function async_run_notebook (line 150) | async def async_run_notebook(filename, opts, resources=None):
  function prepare_cell_mocks (line 175) | def prepare_cell_mocks(*messages_input, reply_msg=None):
  function normalize_output (line 252) | def normalize_output(output):
  function assert_notebooks_equal (line 281) | def assert_notebooks_equal(expected, actual):
  function notebook_resources (line 302) | def notebook_resources():
  function filter_messages_on_error_output (line 310) | def filter_messages_on_error_output(err_output):
  function test_run_all_notebooks (line 350) | def test_run_all_notebooks(input_name, opts):
  function test_parallel_notebooks (line 358) | def test_parallel_notebooks(capfd, tmpdir):
  function test_many_parallel_notebooks (line 385) | def test_many_parallel_notebooks(capfd):
  function test_async_parallel_notebooks (line 412) | def test_async_parallel_notebooks(capfd, tmpdir):
  function test_many_async_parallel_notebooks (line 439) | def test_many_async_parallel_notebooks(capfd):
  function test_execution_timing (line 464) | def test_execution_timing():
  function test_synchronous_setup_kernel (line 494) | def test_synchronous_setup_kernel():
  function test_startnewkernel_with_kernelmanager (line 504) | def test_startnewkernel_with_kernelmanager():
  function test_start_new_kernel_history_file_setting (line 519) | def test_start_new_kernel_history_file_setting():
  function test_start_new_kernel_client_cleans_up_kernel_on_failure (line 542) | def test_start_new_kernel_client_cleans_up_kernel_on_failure():
  class TestExecute (line 574) | class TestExecute(NBClientTestsBase):
    method test_constructor (line 579) | def test_constructor(self):
    method test_populate_language_info (line 582) | def test_populate_language_info(self):
    method test_empty_path (line 588) | def test_empty_path(self):
    method test_empty_kernel_name (line 600) | def test_empty_kernel_name(self):
    method test_disable_stdin (line 613) | def test_disable_stdin(self):
    method test_timeout (line 634) | def test_timeout(self):
    method test_timeout_func (line 653) | def test_timeout_func(self):
    method test_sync_kernel_manager (line 665) | def test_sync_kernel_manager(self):
    method test_kernel_death_after_timeout (line 677) | def test_kernel_death_after_timeout(self):
    method test_kernel_death_during_execution (line 701) | def test_kernel_death_during_execution(self):
    method test_allow_errors (line 714) | def test_allow_errors(self):
    method test_force_raise_errors (line 731) | def test_force_raise_errors(self):
    method test_reset_kernel_client (line 759) | def test_reset_kernel_client(self):
    method test_cleanup_kernel_client (line 784) | def test_cleanup_kernel_client(self):
    method test_custom_kernel_manager (line 804) | def test_custom_kernel_manager(self):
    method test_process_message_wrapper (line 833) | def test_process_message_wrapper(self):
    method test_execute_function (line 855) | def test_execute_function(self):
    method test_widgets (line 866) | def test_widgets(self):
    method test_execution_hook (line 893) | def test_execution_hook(self):
    method test_error_execution_hook_error (line 908) | def test_error_execution_hook_error(self):
    method test_error_notebook_hook (line 924) | def test_error_notebook_hook(self):
    method test_async_execution_hook (line 940) | def test_async_execution_hook(self):
    method test_error_async_execution_hook (line 955) | def test_error_async_execution_hook(self):
  class TestRunCell (line 972) | class TestRunCell(NBClientTestsBase):
    method test_idle_message (line 976) | def test_idle_message(self, executor, cell_mock, message_mock):
    method test_message_for_wrong_parent (line 991) | def test_message_for_wrong_parent(self, executor, cell_mock, message_m...
    method test_busy_message (line 1005) | def test_busy_message(self, executor, cell_mock, message_mock):
    method test_deadline_exec_reply (line 1024) | def test_deadline_exec_reply(self, executor, cell_mock, message_mock):
    method test_deadline_iopub (line 1047) | def test_deadline_iopub(self, executor, cell_mock, message_mock):
    method test_eventual_deadline_iopub (line 1067) | def test_eventual_deadline_iopub(self, executor, cell_mock, message_mo...
    method test_execute_input_message (line 1096) | def test_execute_input_message(self, executor, cell_mock, message_mock):
    method test_stream_messages (line 1115) | def test_stream_messages(self, executor, cell_mock, message_mock):
    method test_clear_output_message (line 1136) | def test_clear_output_message(self, executor, cell_mock, message_mock):
    method test_clear_output_wait_message (line 1155) | def test_clear_output_wait_message(self, executor, cell_mock, message_...
    method test_clear_output_wait_then_message_message (line 1181) | def test_clear_output_wait_then_message_message(self, executor, cell_m...
    method test_clear_output_wait_then_update_display_message (line 1207) | def test_clear_output_wait_then_update_display_message(self, executor,...
    method test_execution_count_message (line 1223) | def test_execution_count_message(self, executor, cell_mock, message_mo...
    method test_execution_count_message_ignored_on_override (line 1238) | def test_execution_count_message_ignored_on_override(self, executor, c...
    method test_execution_count_with_stream_message (line 1253) | def test_execution_count_with_stream_message(self, executor, cell_mock...
    method test_widget_comm_message (line 1268) | def test_widget_comm_message(self, executor, cell_mock, message_mock):
    method test_widget_comm_buffer_message_single (line 1289) | def test_widget_comm_buffer_message_single(self, executor, cell_mock, ...
    method test_widget_comm_buffer_messages (line 1320) | def test_widget_comm_buffer_messages(self, executor, cell_mock, messag...
    method test_unknown_comm_message (line 1345) | def test_unknown_comm_message(self, executor, cell_mock, message_mock):
    method test_execute_result_message (line 1366) | def test_execute_result_message(self, executor, cell_mock, message_mock):
    method test_execute_result_with_display_message (line 1395) | def test_execute_result_with_display_message(self, executor, cell_mock...
    method test_display_data_without_id_message (line 1418) | def test_display_data_without_id_message(self, executor, cell_mock, me...
    method test_display_data_message (line 1444) | def test_display_data_message(self, executor, cell_mock, message_mock):
    method test_display_data_same_id_message (line 1487) | def test_display_data_same_id_message(self, executor, cell_mock, messa...
    method test_update_display_data_without_id_message (line 1518) | def test_update_display_data_without_id_message(self, executor, cell_m...
    method test_update_display_data_mismatch_id_message (line 1547) | def test_update_display_data_mismatch_id_message(self, executor, cell_...
    method test_update_display_data_message (line 1581) | def test_update_display_data_message(self, executor, cell_mock, messag...
    method test_error_message (line 1602) | def test_error_message(self, executor, cell_mock, message_mock):
    method test_error_and_error_status_messages (line 1624) | def test_error_and_error_status_messages(self, executor, cell_mock, me...
    method test_error_message_only (line 1648) | def test_error_message_only(self, executor, cell_mock, message_mock):
    method test_allow_errors (line 1667) | def test_allow_errors(self, executor, cell_mock, message_mock):
    method test_allow_error_names (line 1685) | def test_allow_error_names(self, executor, cell_mock, message_mock):
    method test_raises_exception_tag (line 1703) | def test_raises_exception_tag(self, executor, cell_mock, message_mock):
    method test_non_code_cell (line 1721) | def test_non_code_cell(self, executor, cell_mock, message_mock):
    method test_no_source (line 1739) | def test_no_source(self, executor, cell_mock, message_mock):
    method test_cell_hooks (line 1756) | def test_cell_hooks(self, executor, cell_mock, message_mock):
    method test_error_cell_hooks (line 1783) | def test_error_cell_hooks(self, executor, cell_mock, message_mock):
    method test_non_code_cell_hooks (line 1808) | def test_non_code_cell_hooks(self, executor, cell_mock, message_mock):
    method test_async_cell_hooks (line 1822) | def test_async_cell_hooks(self, executor, cell_mock, message_mock):
    method test_error_async_cell_hooks (line 1849) | def test_error_async_cell_hooks(self, executor, cell_mock, message_mock):
    method test_coalesce_streams (line 1888) | def test_coalesce_streams(self, executor, cell_mock, message_mock):

FILE: tests/test_util.py
  function some_async_function (line 13) | async def some_async_function():
  function test_nested_asyncio_with_existing_ioloop (line 18) | def test_nested_asyncio_with_existing_ioloop():
  function test_nested_asyncio_with_no_ioloop (line 29) | def test_nested_asyncio_with_no_ioloop():
  function test_nested_asyncio_with_tornado (line 34) | def test_nested_asyncio_with_tornado():
  function test_run_hook_sync (line 59) | async def test_run_hook_sync():
  function test_run_hook_async (line 66) | async def test_run_hook_async():
Condensed preview — 69 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (333K chars).
[
  {
    "path": ".github/dependabot.yml",
    "chars": 346,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n  "
  },
  {
    "path": ".github/workflows/check-release.yml",
    "chars": 589,
    "preview": "name: Check Release\non:\n  push:\n    branches: [\"*\"]\n  pull_request:\n    branches: [\"*\"]\n  release:\n    types: [published"
  },
  {
    "path": ".github/workflows/main.yml",
    "chars": 4097,
    "preview": "name: CI\n\non:\n  push:\n    branches: [\"main\"]\n  pull_request:\n  workflow_dispatch:\n  schedule:\n    - cron: \"0 8 * * *\"\n\nd"
  },
  {
    "path": ".github/workflows/prep-release.yml",
    "chars": 1689,
    "preview": "name: \"Step 1: Prep Release\"\non:\n  workflow_dispatch:\n    inputs:\n      version_spec:\n        description: \"New Version "
  },
  {
    "path": ".github/workflows/publish-changelog.yml",
    "chars": 928,
    "preview": "name: \"Publish Changelog\"\non:\n  release:\n    types: [published]\n\n  workflow_dispatch:\n    inputs:\n      branch:\n        "
  },
  {
    "path": ".github/workflows/publish-release.yml",
    "chars": 1798,
    "preview": "name: \"Step 2: Publish Release\"\non:\n  workflow_dispatch:\n    inputs:\n      branch:\n        description: \"The target bran"
  },
  {
    "path": ".gitignore",
    "chars": 1940,
    "preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packagi"
  },
  {
    "path": ".pre-commit-config.yaml",
    "chars": 2411,
    "preview": "ci:\n  autoupdate_schedule: monthly\n  autoupdate_commit_msg: \"chore: update pre-commit hooks\"\n\nrepos:\n  - repo: https://g"
  },
  {
    "path": ".readthedocs.yml",
    "chars": 354,
    "preview": "# .readthedocs.yml\n# Read the Docs configuration file\n# See https://docs.readthedocs.io/en/stable/config-file/v2.html fo"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 37652,
    "preview": "# Changes in NBClient {#changelog}\n\n<!-- <START NEW CHANGELOG ENTRY> -->\n\n## 0.10.4\n\n([Full Changelog](https://github.co"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 1221,
    "preview": "# Contributing\n\nWe follow the [Jupyter Contribution Workflow](https://jupyter.readthedocs.io/en/latest/contributing/cont"
  },
  {
    "path": "LICENSE",
    "chars": 1534,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2020-, Jupyter Development Team\n\nAll rights reserved.\n\nRedistribution and use in sou"
  },
  {
    "path": "README.md",
    "chars": 3882,
    "preview": "[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/jupyter/nbclient/main?filepath=binder%2Frun_"
  },
  {
    "path": "RELEASING.md",
    "chars": 640,
    "preview": "# Releasing\n\n## Using `jupyter_releaser`\n\nThe recommended way to make a release is to use [`jupyter_releaser`](https://g"
  },
  {
    "path": "binder/empty_notebook.ipynb",
    "chars": 1859,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Show a pandas dataframe\"\n   ]\n  }"
  },
  {
    "path": "binder/environment.yml",
    "chars": 131,
    "preview": "name: nbclient\nchannels:\n  - conda-forge\ndependencies:\n  - numpy\n  - pandas\n  - matplotlib\n  - scrapbook\n  - nbformat\n  "
  },
  {
    "path": "binder/run_nbclient.ipynb",
    "chars": 2827,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": "
  },
  {
    "path": "docs/Makefile",
    "chars": 606,
    "preview": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHI"
  },
  {
    "path": "docs/UPDATE.md",
    "chars": 108,
    "preview": "TODO: Figure out make options needed for non-api changes\n\n```\nsphinx-apidoc -f -o reference ../nbclient\n```\n"
  },
  {
    "path": "docs/_static/custom.css",
    "chars": 450,
    "preview": "img.logo {\n  width:100%\n}\n\n.right-next {\n    float: right;\n    max-width: 45%;\n    overflow: auto;\n    text-overflow: el"
  },
  {
    "path": "docs/autogen_config.py",
    "chars": 1051,
    "preview": "#!/usr/bin/env python\n\"\"\"\nautogen_config.py\n\nCreate config_options.rst, a Sphinx documentation source file.\nDocuments th"
  },
  {
    "path": "docs/client.rst",
    "chars": 10822,
    "preview": "Executing notebooks\n===================\n\n.. module:: nbclient.client.guide\n\nJupyter notebooks are often saved with outpu"
  },
  {
    "path": "docs/conf.py",
    "chars": 6043,
    "preview": "#!/usr/bin/env python3\n#\n# nbclient documentation build configuration file, created by\n# sphinx-quickstart on Mon Jan 26"
  },
  {
    "path": "docs/index.rst",
    "chars": 2033,
    "preview": "Welcome to nbclient\n===================\n\n.. image:: https://img.shields.io/github/stars/jupyter/nbclient?label=stars&sty"
  },
  {
    "path": "docs/installation.rst",
    "chars": 295,
    "preview": "Installation\n============\n\nInstalling nbclient\n-------------------\n\nFrom the command line:\n\n.. code-block:: bash\n\n   pyt"
  },
  {
    "path": "docs/make.bat",
    "chars": 776,
    "preview": "@ECHO OFF\n\npushd %~dp0\n\nREM Command file for Sphinx documentation\n\nif \"%SPHINXBUILD%\" == \"\" (\n\tset SPHINXBUILD=sphinx-bu"
  },
  {
    "path": "docs/reference/index.rst",
    "chars": 190,
    "preview": "Reference\n=========\n\nThis part of the documentation lists the full API reference of all public classes and functions.\n\n."
  },
  {
    "path": "docs/reference/modules.rst",
    "chars": 61,
    "preview": "nbclient\n========\n\n.. toctree::\n   :maxdepth: 4\n\n   nbclient\n"
  },
  {
    "path": "docs/reference/nbclient.rst",
    "chars": 476,
    "preview": "nbclient package\n================\n\nSubpackages\n-----------\n\nSubmodules\n----------\n\nnbclient.client module\n--------------"
  },
  {
    "path": "nbclient/__init__.py",
    "chars": 164,
    "preview": "from ._version import __version__, version_info\nfrom .client import NotebookClient, execute\n\n__all__ = [\"__version__\", \""
  },
  {
    "path": "nbclient/_version.py",
    "chars": 463,
    "preview": "\"\"\"Version info.\"\"\"\nfrom __future__ import annotations\n\nimport re\n\n__version__ = \"0.10.4\"\n\n# Build up version_info tuple"
  },
  {
    "path": "nbclient/cli.py",
    "chars": 6781,
    "preview": "\"\"\"nbclient cli.\"\"\"\nfrom __future__ import annotations\n\nimport logging\nimport sys\nimport typing\nfrom pathlib import Path"
  },
  {
    "path": "nbclient/client.py",
    "chars": 49883,
    "preview": "\"\"\"nbclient implementation.\"\"\"\nfrom __future__ import annotations\n\nimport asyncio\nimport atexit\nimport base64\nimport col"
  },
  {
    "path": "nbclient/exceptions.py",
    "chars": 4065,
    "preview": "\"\"\"Exceptions for nbclient.\"\"\"\nfrom __future__ import annotations\n\nfrom typing import Any\n\nfrom nbformat import Notebook"
  },
  {
    "path": "nbclient/jsonutil.py",
    "chars": 4612,
    "preview": "\"\"\"Utilities to manipulate JSON objects.\"\"\"\n\n# NOTE: this is a copy of ipykernel/jsonutils.py (+blackified)\n\n# Copyright"
  },
  {
    "path": "nbclient/output_widget.py",
    "chars": 4534,
    "preview": "\"\"\"An output widget mimic.\"\"\"\nfrom __future__ import annotations\n\nfrom typing import Any\n\nfrom jupyter_client.client imp"
  },
  {
    "path": "nbclient/py.typed",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "nbclient/util.py",
    "chars": 571,
    "preview": "\"\"\"General utility methods\"\"\"\n\n# Copyright (c) Jupyter Development Team.\n# Distributed under the terms of the Modified B"
  },
  {
    "path": "pyproject.toml",
    "chars": 5415,
    "preview": "[build-system]\nrequires = [\n    \"hatchling>=1.10.0\",\n]\nbuild-backend = \"hatchling.build\"\n\n[project]\nname = \"nbclient\"\ndy"
  },
  {
    "path": "tests/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/base.py",
    "chars": 2011,
    "preview": "import unittest\n\nfrom nbformat import v4 as nbformat\n\n# mypy: disable-error-code=\"no-untyped-call,no-untyped-def\"\n\n\nclas"
  },
  {
    "path": "tests/conftest.py",
    "chars": 429,
    "preview": "import asyncio\nimport os\n\n# This is important for ipykernel to show the same string\n# instead of randomly generated file"
  },
  {
    "path": "tests/fake_kernelmanager.py",
    "chars": 865,
    "preview": "from jupyter_client.manager import AsyncKernelManager\n\n# mypy: disable-error-code=\"no-untyped-call,no-untyped-def\"\n\n\ncla"
  },
  {
    "path": "tests/files/Autokill.ipynb",
    "chars": 673,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": "
  },
  {
    "path": "tests/files/Check History in Memory.ipynb",
    "chars": 452,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n "
  },
  {
    "path": "tests/files/Clear Output.ipynb",
    "chars": 3697,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n "
  },
  {
    "path": "tests/files/Disable Stdin.ipynb",
    "chars": 339,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \""
  },
  {
    "path": "tests/files/Empty Cell.ipynb",
    "chars": 1291,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Test that executing skips over an e"
  },
  {
    "path": "tests/files/Error.ipynb",
    "chars": 1293,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"id\": \"d200673b\",\n   \"metadata\": {},\n   \"outputs\":"
  },
  {
    "path": "tests/files/Factorials.ipynb",
    "chars": 679,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"out"
  },
  {
    "path": "tests/files/HelloWorld.ipynb",
    "chars": 357,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"out"
  },
  {
    "path": "tests/files/Inline Image.ipynb",
    "chars": 14335,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"out"
  },
  {
    "path": "tests/files/Interrupt.ipynb",
    "chars": 1328,
    "preview": "{\n    \"cells\": [\n     {\n      \"cell_type\": \"code\",\n      \"execution_count\": 1,\n      \"metadata\": {\n       \"collapsed\": f"
  },
  {
    "path": "tests/files/JupyterWidgets.ipynb",
    "chars": 2155,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"data\":"
  },
  {
    "path": "tests/files/Other Comms.ipynb",
    "chars": 1333,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {\n    \"ExecuteTime\": {\n     \"end_time\""
  },
  {
    "path": "tests/files/Output.ipynb",
    "chars": 20404,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"data\":"
  },
  {
    "path": "tests/files/Parallel Execute A.ipynb",
    "chars": 2429,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Ensure notebooks can execute in p"
  },
  {
    "path": "tests/files/Parallel Execute B.ipynb",
    "chars": 2429,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Ensure notebooks can execute in p"
  },
  {
    "path": "tests/files/SVG.ipynb",
    "chars": 1008,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"out"
  },
  {
    "path": "tests/files/Skip Exceptions with Cell Tags.ipynb",
    "chars": 2552,
    "preview": "{\n    \"cells\": [\n     {\n      \"cell_type\": \"code\",\n      \"execution_count\": 1,\n      \"metadata\": {\n       \"tags\": [\n    "
  },
  {
    "path": "tests/files/Skip Exceptions.ipynb",
    "chars": 1343,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"out"
  },
  {
    "path": "tests/files/Skip Execution with Cell Tag.ipynb",
    "chars": 513,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"tags\": [\n     \"skip-executio"
  },
  {
    "path": "tests/files/Sleep1s.ipynb",
    "chars": 1203,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": "
  },
  {
    "path": "tests/files/Unicode.ipynb",
    "chars": 337,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"out"
  },
  {
    "path": "tests/files/UnicodePy3.ipynb",
    "chars": 438,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"out"
  },
  {
    "path": "tests/files/update-display-id.ipynb",
    "chars": 3468,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outp"
  },
  {
    "path": "tests/test_cli.py",
    "chars": 5368,
    "preview": "import sys\nfrom pathlib import Path\nfrom subprocess import CalledProcessError, check_output\nfrom unittest.mock import ca"
  },
  {
    "path": "tests/test_client.py",
    "chars": 71550,
    "preview": "from __future__ import annotations\n\nimport asyncio\nimport concurrent.futures\nimport copy\nimport datetime\nimport functool"
  },
  {
    "path": "tests/test_util.py",
    "chars": 1856,
    "preview": "import asyncio\nfrom unittest.mock import MagicMock\n\nimport pytest\nimport tornado\n\nfrom nbclient.util import run_hook, ru"
  }
]

About this extraction

This page contains the full source code of the jupyter/nbclient GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 69 files (302.2 KB), approximately 86.6k tokens, and a symbol index with 175 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!