pax_global_header00006660000000000000000000000064145740642040014520gustar00rootroot0000000000000052 comment=7423ddd21abb7f4e5b045275bf8145f1d895fa04 jupyter_server_terminals-0.5.3/000077500000000000000000000000001457406420400166735ustar00rootroot00000000000000jupyter_server_terminals-0.5.3/.gitconfig000066400000000000000000000000641457406420400206450ustar00rootroot00000000000000[blame] ignoreRevsFile = .git-blame-ignore-revs jupyter_server_terminals-0.5.3/.github/000077500000000000000000000000001457406420400202335ustar00rootroot00000000000000jupyter_server_terminals-0.5.3/.github/dependabot.yml000066400000000000000000000005071457406420400230650ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" groups: actions: patterns: - "*" - package-ecosystem: "pip" directory: "/" schedule: interval: "weekly" groups: actions: patterns: - "*" jupyter_server_terminals-0.5.3/.github/workflows/000077500000000000000000000000001457406420400222705ustar00rootroot00000000000000jupyter_server_terminals-0.5.3/.github/workflows/enforce-label.yml000066400000000000000000000005001457406420400255040ustar00rootroot00000000000000name: Enforce PR label on: pull_request: types: [labeled, unlabeled, opened, edited, synchronize] jobs: enforce-label: runs-on: ubuntu-latest permissions: pull-requests: write steps: - name: enforce-triage-label uses: jupyterlab/maintainer-tools/.github/actions/enforce-label@v1 jupyter_server_terminals-0.5.3/.github/workflows/prep-release.yml000066400000000000000000000032311457406420400253760ustar00rootroot00000000000000name: "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 }}" jupyter_server_terminals-0.5.3/.github/workflows/publish-changelog.yml000066400000000000000000000016401457406420400264070ustar00rootroot00000000000000name: "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 }}" jupyter_server_terminals-0.5.3/.github/workflows/publish-release.yml000066400000000000000000000034061457406420400261020ustar00rootroot00000000000000name: "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 }} jupyter_server_terminals-0.5.3/.github/workflows/test.yml000066400000000000000000000110121457406420400237650ustar00rootroot00000000000000name: Tests on: push: branches: ["main"] pull_request: 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 . pipx run doc8 --max-line-length=200 test: runs-on: ${{ matrix.os }} timeout-minutes: 10 strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] python-version: ["3.8", "3.12"] include: - os: windows-latest python-version: "3.9" - os: ubuntu-latest python-version: "pypy-3.9" - os: macos-latest python-version: "3.10" - os: ubuntu-latest python-version: "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 on pypy if: ${{ startsWith(matrix.python-version, 'pypy') }} run: | hatch run test:nowarn || hatch run test:nowarn --lf - name: Run the tests if: ${{ !startsWith(matrix.python-version, 'pypy') }} run: | hatch run cov:test --cov-fail-under 75 || hatch run test:test --lf - uses: jupyterlab/maintainer-tools/.github/actions/upload-coverage@v1 coverage: runs-on: ubuntu-latest needs: - test steps: - uses: actions/checkout@v4 - uses: jupyterlab/maintainer-tools/.github/actions/report-coverage@v1 test_docs: name: Test Docs runs-on: windows-latest steps: - uses: actions/checkout@v4 - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 - run: hatch run docs:build 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 -vv run test:nowarn || hatch 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: 15 steps: - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 - uses: jupyterlab/maintainer-tools/.github/actions/test-sdist@v1 with: extra_test: 'jupyter server extension list 2>&1 | grep -ie "jupyter_server_terminals.*OK"' check_release: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Base Setup uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 - name: Install Dependencies run: | pip install -e . - name: Check Release uses: jupyter-server/jupyter_releaser/.github/actions/check-release@v2 with: token: ${{ secrets.GITHUB_TOKEN }} check_links: 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/check-links@v1 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) }} jupyter_server_terminals-0.5.3/.gitignore000066400000000000000000000006571457406420400206730ustar00rootroot00000000000000MANIFEST build dist _build docs/man/*.gz docs/source/api/generated docs/source/config.rst docs/gh-pages node_modules *.py[co] __pycache__ *.egg-info *~ *.bak .ipynb_checkpoints .tox .DS_Store \#*# .#* .coverage* .pytest_cache src *.swp *.map Read the Docs config.rst docs/source/changelog.md /.project /.pydevproject # jetbrains ide stuff *.iml .idea/ # vscode ide stuff *.code-workspace .history .vscode/* !.vscode/*.template jupyter_server_terminals-0.5.3/.pre-commit-config.yaml000066400000000000000000000042001457406420400231500ustar00rootroot00000000000000ci: 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 - 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.8.0" hooks: - id: mypy files: "^jupyter_server_terminals" stages: [manual] args: ["--install-types", "--non-interactive"] additional_dependencies: ["traitlets>=5.13", "jupyter_server>=2.10.1", "terminado>=0.18"] - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.2.0 hooks: - id: ruff types_or: [python, jupyter] args: ["--fix", "--show-fixes"] - id: ruff-format types_or: [python, jupyter] - repo: https://github.com/scientific-python/cookie rev: "2024.01.24" hooks: - id: sp-repo-review additional_dependencies: ["repo-review[cli]"] jupyter_server_terminals-0.5.3/.readthedocs.yaml000066400000000000000000000003621457406420400221230ustar00rootroot00000000000000version: 2 sphinx: configuration: docs/source/conf.py python: install: # install itself with pip install . - method: pip path: . extra_requirements: - docs build: os: ubuntu-22.04 tools: python: "3.11" jupyter_server_terminals-0.5.3/CHANGELOG.md000066400000000000000000000622601457406420400205120ustar00rootroot00000000000000# Changelog All notable changes to this project will be documented in this file. ## 0.5.3 ([Full Changelog](https://github.com/jupyter-server/jupyter_server_terminals/compare/v0.5.2...4d5e3041fe8b24511d0b78b99a7678e353b78612)) ### Maintenance and upkeep improvements - Update Release Scripts [#108](https://github.com/jupyter-server/jupyter_server_terminals/pull/108) ([@blink1073](https://github.com/blink1073)) - chore: update pre-commit hooks [#107](https://github.com/jupyter-server/jupyter_server_terminals/pull/107) ([@pre-commit-ci](https://github.com/pre-commit-ci)) ### Contributors to this release ([GitHub contributors page for this release](https://github.com/jupyter-server/jupyter_server_terminals/graphs/contributors?from=2024-01-22&to=2024-03-12&type=c)) [@blink1073](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Ablink1073+updated%3A2024-01-22..2024-03-12&type=Issues) | [@pre-commit-ci](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Apre-commit-ci+updated%3A2024-01-22..2024-03-12&type=Issues) ## 0.5.2 ([Full Changelog](https://github.com/jupyter-server/jupyter_server_terminals/compare/v0.5.1...1d47163a9c02f75ff24943faa6a69bf8639b3517)) ### Bugs fixed - Fix usage of await [#106](https://github.com/jupyter-server/jupyter_server_terminals/pull/106) ([@blink1073](https://github.com/blink1073)) - Set terminals_available to False when not enabled [#105](https://github.com/jupyter-server/jupyter_server_terminals/pull/105) ([@Wh1isper](https://github.com/Wh1isper)) ### Maintenance and upkeep improvements - chore: update pre-commit hooks [#104](https://github.com/jupyter-server/jupyter_server_terminals/pull/104) ([@pre-commit-ci](https://github.com/pre-commit-ci)) ### Contributors to this release ([GitHub contributors page for this release](https://github.com/jupyter-server/jupyter_server_terminals/graphs/contributors?from=2023-12-26&to=2024-01-22&type=c)) [@blink1073](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Ablink1073+updated%3A2023-12-26..2024-01-22&type=Issues) | [@pre-commit-ci](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Apre-commit-ci+updated%3A2023-12-26..2024-01-22&type=Issues) | [@Wh1isper](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3AWh1isper+updated%3A2023-12-26..2024-01-22&type=Issues) ## 0.5.1 ([Full Changelog](https://github.com/jupyter-server/jupyter_server_terminals/compare/v0.5.0...58ad66a5ce0bca03f6c569b32a8bef9c1bdccd2b)) ### Bugs fixed - fix: support OPTIONS method for CORS [#102](https://github.com/jupyter-server/jupyter_server_terminals/pull/102) ([@zhanba](https://github.com/zhanba)) ### Contributors to this release ([GitHub contributors page for this release](https://github.com/jupyter-server/jupyter_server_terminals/graphs/contributors?from=2023-12-11&to=2023-12-26&type=c)) [@welcome](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Awelcome+updated%3A2023-12-11..2023-12-26&type=Issues) | [@zhanba](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Azhanba+updated%3A2023-12-11..2023-12-26&type=Issues) ## 0.5.0 ([Full Changelog](https://github.com/jupyter-server/jupyter_server_terminals/compare/v0.4.4...00eb4ee20b8d5838e44de7a756823e4cf02949fa)) ### Bugs fixed - Fix respecting serverapp.terminals_enabled [#91](https://github.com/jupyter-server/jupyter_server_terminals/pull/91) ([@danielzgtg](https://github.com/danielzgtg)) ### Maintenance and upkeep improvements - Update ruff config [#101](https://github.com/jupyter-server/jupyter_server_terminals/pull/101) ([@blink1073](https://github.com/blink1073)) - Update typings for Server 2.10.1 and mypy 1.7 #425 [#99](https://github.com/jupyter-server/jupyter_server_terminals/pull/99) ([@blink1073](https://github.com/blink1073)) - Update types for terminado 0.18 [#98](https://github.com/jupyter-server/jupyter_server_terminals/pull/98) ([@blink1073](https://github.com/blink1073)) - Update ruff config [#97](https://github.com/jupyter-server/jupyter_server_terminals/pull/97) ([@blink1073](https://github.com/blink1073)) - Update typings for server 2.10 [#96](https://github.com/jupyter-server/jupyter_server_terminals/pull/96) ([@blink1073](https://github.com/blink1073)) - chore: update pre-commit hooks [#95](https://github.com/jupyter-server/jupyter_server_terminals/pull/95) ([@pre-commit-ci](https://github.com/pre-commit-ci)) - Clean up lint handling [#94](https://github.com/jupyter-server/jupyter_server_terminals/pull/94) ([@blink1073](https://github.com/blink1073)) - Adopt ruff format [#93](https://github.com/jupyter-server/jupyter_server_terminals/pull/93) ([@blink1073](https://github.com/blink1073)) - Update ruff and typing [#92](https://github.com/jupyter-server/jupyter_server_terminals/pull/92) ([@blink1073](https://github.com/blink1073)) - chore: update pre-commit hooks [#90](https://github.com/jupyter-server/jupyter_server_terminals/pull/90) ([@pre-commit-ci](https://github.com/pre-commit-ci)) - Fix typings for traitlets 5.10.1 [#89](https://github.com/jupyter-server/jupyter_server_terminals/pull/89) ([@blink1073](https://github.com/blink1073)) - Bump actions/checkout from 3 to 4 [#88](https://github.com/jupyter-server/jupyter_server_terminals/pull/88) ([@dependabot](https://github.com/dependabot)) - Adopt sp-repo-review [#87](https://github.com/jupyter-server/jupyter_server_terminals/pull/87) ([@blink1073](https://github.com/blink1073)) - Update mistune requirement from \<3.0 to \<4.0 [#83](https://github.com/jupyter-server/jupyter_server_terminals/pull/83) ([@dependabot](https://github.com/dependabot)) - Use local coverage [#80](https://github.com/jupyter-server/jupyter_server_terminals/pull/80) ([@blink1073](https://github.com/blink1073)) - Clean up license [#77](https://github.com/jupyter-server/jupyter_server_terminals/pull/77) ([@dcsaba89](https://github.com/dcsaba89)) - Add more linting [#75](https://github.com/jupyter-server/jupyter_server_terminals/pull/75) ([@blink1073](https://github.com/blink1073)) ### Contributors to this release ([GitHub contributors page for this release](https://github.com/jupyter-server/jupyter_server_terminals/graphs/contributors?from=2023-01-09&to=2023-12-11&type=c)) [@blink1073](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Ablink1073+updated%3A2023-01-09..2023-12-11&type=Issues) | [@codecov](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Acodecov+updated%3A2023-01-09..2023-12-11&type=Issues) | [@danielzgtg](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Adanielzgtg+updated%3A2023-01-09..2023-12-11&type=Issues) | [@dcsaba89](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Adcsaba89+updated%3A2023-01-09..2023-12-11&type=Issues) | [@dependabot](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Adependabot+updated%3A2023-01-09..2023-12-11&type=Issues) | [@pre-commit-ci](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Apre-commit-ci+updated%3A2023-01-09..2023-12-11&type=Issues) | [@welcome](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Awelcome+updated%3A2023-01-09..2023-12-11&type=Issues) ## 0.4.4 ([Full Changelog](https://github.com/jupyter-server/jupyter_server_terminals/compare/v0.4.3...6791413888e45d2aeab5d9d154c98ca9dbd828d8)) ### Maintenance and upkeep improvements - Add typing file [#74](https://github.com/jupyter-server/jupyter_server_terminals/pull/74) ([@blink1073](https://github.com/blink1073)) - Add spelling and docstring enforcement [#72](https://github.com/jupyter-server/jupyter_server_terminals/pull/72) ([@blink1073](https://github.com/blink1073)) ### Contributors to this release ([GitHub contributors page for this release](https://github.com/jupyter-server/jupyter_server_terminals/graphs/contributors?from=2022-12-19&to=2023-01-09&type=c)) [@blink1073](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Ablink1073+updated%3A2022-12-19..2023-01-09&type=Issues) | [@codecov](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Acodecov+updated%3A2022-12-19..2023-01-09&type=Issues) | [@pre-commit-ci](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Apre-commit-ci+updated%3A2022-12-19..2023-01-09&type=Issues) ## 0.4.3 ([Full Changelog](https://github.com/jupyter-server/jupyter_server_terminals/compare/v0.4.2...b1f2a99b062192e809d770c517ce02988d32d121)) ### Bugs fixed - Fix Server Version Handling and Clean up CI [#71](https://github.com/jupyter-server/jupyter_server_terminals/pull/71) ([@blink1073](https://github.com/blink1073)) ### Maintenance and upkeep improvements - Update mistune requirement from \<2.0 to \<3.0 [#70](https://github.com/jupyter-server/jupyter_server_terminals/pull/70) ([@dependabot](https://github.com/dependabot)) - Adopt ruff and address lint [#69](https://github.com/jupyter-server/jupyter_server_terminals/pull/69) ([@blink1073](https://github.com/blink1073)) ### Contributors to this release ([GitHub contributors page for this release](https://github.com/jupyter-server/jupyter_server_terminals/graphs/contributors?from=2022-12-01&to=2022-12-19&type=c)) [@blink1073](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Ablink1073+updated%3A2022-12-01..2022-12-19&type=Issues) | [@codecov](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Acodecov+updated%3A2022-12-01..2022-12-19&type=Issues) | [@dependabot](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Adependabot+updated%3A2022-12-01..2022-12-19&type=Issues) | [@pre-commit-ci](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Apre-commit-ci+updated%3A2022-12-01..2022-12-19&type=Issues) ## 0.4.2 ([Full Changelog](https://github.com/jupyter-server/jupyter_server_terminals/compare/v0.4.1...744451298913d2c2d81698f94d16dfb595df897f)) ### Maintenance and upkeep improvements - Use pytest-jupyter [#67](https://github.com/jupyter-server/jupyter_server_terminals/pull/67) ([@blink1073](https://github.com/blink1073)) - Fixup workflows and add badges [#64](https://github.com/jupyter-server/jupyter_server_terminals/pull/64) ([@blink1073](https://github.com/blink1073)) ### Documentation improvements - Fixup workflows and add badges [#64](https://github.com/jupyter-server/jupyter_server_terminals/pull/64) ([@blink1073](https://github.com/blink1073)) ### Contributors to this release ([GitHub contributors page for this release](https://github.com/jupyter-server/jupyter_server_terminals/graphs/contributors?from=2022-11-21&to=2022-12-01&type=c)) [@blink1073](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Ablink1073+updated%3A2022-11-21..2022-12-01&type=Issues) | [@codecov](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Acodecov+updated%3A2022-11-21..2022-12-01&type=Issues) | [@pre-commit-ci](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Apre-commit-ci+updated%3A2022-11-21..2022-12-01&type=Issues) ## 0.4.1 ([Full Changelog](https://github.com/jupyter-server/jupyter_server_terminals/compare/v0.4.0...37da434a24475daf674e1711edc53af52dd6957d)) ### Maintenance and upkeep improvements - Switch away from deprecated zmqhandlers module in Jupyter Server 2.0 [#62](https://github.com/jupyter-server/jupyter_server_terminals/pull/62) ([@Zsailer](https://github.com/Zsailer)) - CI Cleanup [#61](https://github.com/jupyter-server/jupyter_server_terminals/pull/61) ([@blink1073](https://github.com/blink1073)) ### Contributors to this release ([GitHub contributors page for this release](https://github.com/jupyter-server/jupyter_server_terminals/graphs/contributors?from=2022-11-11&to=2022-11-21&type=c)) [@blink1073](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Ablink1073+updated%3A2022-11-11..2022-11-21&type=Issues) | [@codecov](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Acodecov+updated%3A2022-11-11..2022-11-21&type=Issues) | [@pre-commit-ci](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Apre-commit-ci+updated%3A2022-11-11..2022-11-21&type=Issues) | [@Zsailer](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3AZsailer+updated%3A2022-11-11..2022-11-21&type=Issues) ## 0.4.0 ([Full Changelog](https://github.com/jupyter-server/jupyter_server_terminals/compare/v0.3.2...6aa63b9c7cfcbe44da7d65964f2b84e0a3a7c83c)) ### Maintenance and upkeep improvements - Add ability to release from repo [#59](https://github.com/jupyter-server/jupyter_server_terminals/pull/59) ([@blink1073](https://github.com/blink1073)) - Handle jupyter core warning [#58](https://github.com/jupyter-server/jupyter_server_terminals/pull/58) ([@blink1073](https://github.com/blink1073)) - Bump actions/checkout from 2 to 3 [#57](https://github.com/jupyter-server/jupyter_server_terminals/pull/57) ([@dependabot](https://github.com/dependabot)) - Add dependabot [#56](https://github.com/jupyter-server/jupyter_server_terminals/pull/56) ([@blink1073](https://github.com/blink1073)) ### Contributors to this release ([GitHub contributors page for this release](https://github.com/jupyter-server/jupyter_server_terminals/graphs/contributors?from=2022-10-31&to=2022-11-11&type=c)) [@blink1073](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Ablink1073+updated%3A2022-10-31..2022-11-11&type=Issues) | [@dependabot](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Adependabot+updated%3A2022-10-31..2022-11-11&type=Issues) | [@pre-commit-ci](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Apre-commit-ci+updated%3A2022-10-31..2022-11-11&type=Issues) ## 0.3.2 ([Full Changelog](https://github.com/jupyter-server/jupyter_server_terminals/compare/v0.3.1...574e1018b7d924f09c1ca45389182f1fd314caee)) ### Maintenance and upkeep improvements - Use ensure_async function [#54](https://github.com/jupyter-server/jupyter_server_terminals/pull/54) ([@blink1073](https://github.com/blink1073)) - Maintenance cleanup [#51](https://github.com/jupyter-server/jupyter_server_terminals/pull/51) ([@blink1073](https://github.com/blink1073)) - Maintenance cleanup [#50](https://github.com/jupyter-server/jupyter_server_terminals/pull/50) ([@blink1073](https://github.com/blink1073)) - Ignore warnings in prerelease test [#47](https://github.com/jupyter-server/jupyter_server_terminals/pull/47) ([@blink1073](https://github.com/blink1073)) - Clean up pyproject and ci [#45](https://github.com/jupyter-server/jupyter_server_terminals/pull/45) ([@blink1073](https://github.com/blink1073)) ### Contributors to this release ([GitHub contributors page for this release](https://github.com/jupyter-server/jupyter_server_terminals/graphs/contributors?from=2022-09-08&to=2022-10-31&type=c)) [@blink1073](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Ablink1073+updated%3A2022-09-08..2022-10-31&type=Issues) | [@codecov-commenter](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Acodecov-commenter+updated%3A2022-09-08..2022-10-31&type=Issues) | [@pre-commit-ci](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Apre-commit-ci+updated%3A2022-09-08..2022-10-31&type=Issues) ## 0.3.1 ([Full Changelog](https://github.com/jupyter-server/jupyter_server_terminals/compare/v0.3.0...6d8b60bc758adc8656ff530a600fb7f57a34259e)) ### Enhancements made - Allow to pass string as a shell override [#42](https://github.com/jupyter-server/jupyter_server_terminals/pull/42) ([@krassowski](https://github.com/krassowski)) ### Maintenance and upkeep improvements - \[pre-commit.ci\] pre-commit autoupdate [#41](https://github.com/jupyter-server/jupyter_server_terminals/pull/41) ([@pre-commit-ci](https://github.com/pre-commit-ci)) - \[pre-commit.ci\] pre-commit autoupdate [#40](https://github.com/jupyter-server/jupyter_server_terminals/pull/40) ([@pre-commit-ci](https://github.com/pre-commit-ci)) - \[pre-commit.ci\] pre-commit autoupdate [#39](https://github.com/jupyter-server/jupyter_server_terminals/pull/39) ([@pre-commit-ci](https://github.com/pre-commit-ci)) - Fix flake8 v5 compat [#38](https://github.com/jupyter-server/jupyter_server_terminals/pull/38) ([@blink1073](https://github.com/blink1073)) - \[pre-commit.ci\] pre-commit autoupdate [#37](https://github.com/jupyter-server/jupyter_server_terminals/pull/37) ([@pre-commit-ci](https://github.com/pre-commit-ci)) - \[pre-commit.ci\] pre-commit autoupdate [#36](https://github.com/jupyter-server/jupyter_server_terminals/pull/36) ([@pre-commit-ci](https://github.com/pre-commit-ci)) - \[pre-commit.ci\] pre-commit autoupdate [#35](https://github.com/jupyter-server/jupyter_server_terminals/pull/35) ([@pre-commit-ci](https://github.com/pre-commit-ci)) - \[pre-commit.ci\] pre-commit autoupdate [#34](https://github.com/jupyter-server/jupyter_server_terminals/pull/34) ([@pre-commit-ci](https://github.com/pre-commit-ci)) - Suppress tornado 6.2 beta warnings [#33](https://github.com/jupyter-server/jupyter_server_terminals/pull/33) ([@blink1073](https://github.com/blink1073)) - \[pre-commit.ci\] pre-commit autoupdate [#32](https://github.com/jupyter-server/jupyter_server_terminals/pull/32) ([@pre-commit-ci](https://github.com/pre-commit-ci)) - \[pre-commit.ci\] pre-commit autoupdate [#31](https://github.com/jupyter-server/jupyter_server_terminals/pull/31) ([@pre-commit-ci](https://github.com/pre-commit-ci)) - \[pre-commit.ci\] pre-commit autoupdate [#30](https://github.com/jupyter-server/jupyter_server_terminals/pull/30) ([@pre-commit-ci](https://github.com/pre-commit-ci)) - Use hatch backend [#29](https://github.com/jupyter-server/jupyter_server_terminals/pull/29) ([@blink1073](https://github.com/blink1073)) - \[pre-commit.ci\] pre-commit autoupdate [#28](https://github.com/jupyter-server/jupyter_server_terminals/pull/28) ([@pre-commit-ci](https://github.com/pre-commit-ci)) - \[pre-commit.ci\] pre-commit autoupdate [#27](https://github.com/jupyter-server/jupyter_server_terminals/pull/27) ([@pre-commit-ci](https://github.com/pre-commit-ci)) ### Contributors to this release ([GitHub contributors page for this release](https://github.com/jupyter-server/jupyter_server_terminals/graphs/contributors?from=2022-05-03&to=2022-09-08&type=c)) [@blink1073](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Ablink1073+updated%3A2022-05-03..2022-09-08&type=Issues) | [@codecov-commenter](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Acodecov-commenter+updated%3A2022-05-03..2022-09-08&type=Issues) | [@krassowski](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Akrassowski+updated%3A2022-05-03..2022-09-08&type=Issues) | [@pre-commit-ci](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Apre-commit-ci+updated%3A2022-05-03..2022-09-08&type=Issues) | [@welcome](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Awelcome+updated%3A2022-05-03..2022-09-08&type=Issues) ## 0.3.0 ([Full Changelog](https://github.com/jupyter-server/jupyter_server_terminals/compare/v0.2.1...3e59b7ca4ebdbf3e1535b4be3a973f2f419ae49f)) ### Maintenance and upkeep improvements - Use alpha release of server 2.0 [#25](https://github.com/jupyter-server/jupyter_server_terminals/pull/25) ([@blink1073](https://github.com/blink1073)) - Switch to flit build backend [#24](https://github.com/jupyter-server/jupyter_server_terminals/pull/24) ([@blink1073](https://github.com/blink1073)) - Allow bot PRs to be auto-labeled [#23](https://github.com/jupyter-server/jupyter_server_terminals/pull/23) ([@blink1073](https://github.com/blink1073)) - \[pre-commit.ci\] pre-commit autoupdate [#22](https://github.com/jupyter-server/jupyter_server_terminals/pull/22) ([@pre-commit-ci](https://github.com/pre-commit-ci)) - \[pre-commit.ci\] pre-commit autoupdate [#21](https://github.com/jupyter-server/jupyter_server_terminals/pull/21) ([@pre-commit-ci](https://github.com/pre-commit-ci)) - Add mypy check [#20](https://github.com/jupyter-server/jupyter_server_terminals/pull/20) ([@blink1073](https://github.com/blink1073)) - \[pre-commit.ci\] pre-commit autoupdate [#19](https://github.com/jupyter-server/jupyter_server_terminals/pull/19) ([@pre-commit-ci](https://github.com/pre-commit-ci)) - Clean up pre-commit [#18](https://github.com/jupyter-server/jupyter_server_terminals/pull/18) ([@blink1073](https://github.com/blink1073)) - \[pre-commit.ci\] pre-commit autoupdate [#17](https://github.com/jupyter-server/jupyter_server_terminals/pull/17) ([@pre-commit-ci](https://github.com/pre-commit-ci)) ### Contributors to this release ([GitHub contributors page for this release](https://github.com/jupyter-server/jupyter_server_terminals/graphs/contributors?from=2022-04-03&to=2022-05-03&type=c)) [@blink1073](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Ablink1073+updated%3A2022-04-03..2022-05-03&type=Issues) | [@pre-commit-ci](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Apre-commit-ci+updated%3A2022-04-03..2022-05-03&type=Issues) ## 0.2.1 ([Full Changelog](https://github.com/jupyter-server/jupyter_server_terminals/compare/v0.2.0...e33b367c81cfb42bf4b903a75f8cd2ae7f400f64)) ### Bugs fixed - Fix initialize method [#15](https://github.com/jupyter-server/jupyter_server_terminals/pull/15) ([@blink1073](https://github.com/blink1073)) ### Contributors to this release ([GitHub contributors page for this release](https://github.com/jupyter-server/jupyter_server_terminals/graphs/contributors?from=2022-04-03&to=2022-04-03&type=c)) [@blink1073](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Ablink1073+updated%3A2022-04-03..2022-04-03&type=Issues) ## 0.2.0 ([Full Changelog](https://github.com/jupyter-server/jupyter_server_terminals/compare/v0.1.0...22d210ea50fb27bcda9a2a781bef6790c509f9a8)) ### Enhancements made - More Cleanup [#12](https://github.com/jupyter-server/jupyter_server_terminals/pull/12) ([@blink1073](https://github.com/blink1073)) - Add authorization [#11](https://github.com/jupyter-server/jupyter_server_terminals/pull/11) ([@blink1073](https://github.com/blink1073)) ### Contributors to this release ([GitHub contributors page for this release](https://github.com/jupyter-server/jupyter_server_terminals/graphs/contributors?from=2022-04-02&to=2022-04-03&type=c)) [@blink1073](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Ablink1073+updated%3A2022-04-02..2022-04-03&type=Issues) ## 0.1.0 ([Full Changelog](https://github.com/jupyter-server/jupyter_server_terminals/compare/0.0.1...c849eb024b37e98004f9f2038a19d2227d0923a4)) ### Enhancements made - update last_activity when pty_read [#4](https://github.com/jupyter-server/jupyter_server_terminals/pull/4) ([@Wh1isper](https://github.com/Wh1isper)) - Refactor terminals into an ExtensionApp [#2](https://github.com/jupyter-server/jupyter_server_terminals/pull/2) ([@Zsailer](https://github.com/Zsailer)) ### Bugs fixed - Pin pywintpy and add missing documentation page [#6](https://github.com/jupyter-server/jupyter_server_terminals/pull/6) ([@blink1073](https://github.com/blink1073)) ### Maintenance and upkeep improvements - More Cleanup [#8](https://github.com/jupyter-server/jupyter_server_terminals/pull/8) ([@blink1073](https://github.com/blink1073)) - Clean up dependencies [#7](https://github.com/jupyter-server/jupyter_server_terminals/pull/7) ([@blink1073](https://github.com/blink1073)) - Initial setup [#1](https://github.com/jupyter-server/jupyter_server_terminals/pull/1) ([@blink1073](https://github.com/blink1073)) ### Documentation improvements - Fix links in readme [#3](https://github.com/jupyter-server/jupyter_server_terminals/pull/3) ([@jasonweill](https://github.com/jasonweill)) ### Contributors to this release ([GitHub contributors page for this release](https://github.com/jupyter-server/jupyter_server_terminals/graphs/contributors?from=2021-12-26&to=2022-04-02&type=c)) [@blink1073](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Ablink1073+updated%3A2021-12-26..2022-04-02&type=Issues) | [@jasonweills](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Ajasonweill+updated%3A2021-12-26..2022-04-02&type=Issues) | [@welcome](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3Awelcome+updated%3A2021-12-26..2022-04-02&type=Issues) | [@Wh1isper](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3AWh1isper+updated%3A2021-12-26..2022-04-02&type=Issues) | [@Zsailer](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server_terminals+involves%3AZsailer+updated%3A2021-12-26..2022-04-02&type=Issues) jupyter_server_terminals-0.5.3/CONTRIBUTING.rst000066400000000000000000000113451457406420400213400ustar00rootroot00000000000000General Jupyter contributor guidelines ====================================== If you're reading this section, you're probably interested in contributing to Jupyter. Welcome and thanks for your interest in contributing! Please take a look at the Contributor documentation, familiarize yourself with using the Jupyter Server, and introduce yourself on the mailing list and share what area of the project you are interested in working on. For general documentation about contributing to Jupyter projects, see the `Project Jupyter Contributor Documentation`__. __ https://jupyter.readthedocs.io/en/latest/contributing/content-contributor.html Setting Up a Development Environment ==================================== Installing Jupyter Server Terminals ----------------------------------- The development version of the server requires `node `_ and `pip `_. Once you have installed the dependencies mentioned above, use the following steps:: pip install --upgrade pip git clone https://github.com/jupyter/jupyter_server_terminals cd jupyter_server_terminals pip install -e . If you are using a system-wide Python installation and you only want to install the server for you, you can add ``--user`` to the install commands. Once you have done this, you can launch the main branch of Jupyter server from any directory in your system with:: jupyter server Code Styling ----------------------------- ``jupyter_server_terminals`` has adopted automatic code formatting so you shouldn't need to worry too much about your code style. As long as your code is valid, the pre-commit hook should take care of how it should look. To install ``pre-commit``, run the following:: pip install pre-commit pre-commit install You can invoke the pre-commit hook by hand at any time with:: pre-commit run which should run any autoformatting on your code and tell you about any errors it couldn't fix automatically. You may also install `black integration `_ into your text editor to format code automatically. If you have already committed files before setting up the pre-commit hook with ``pre-commit install``, you can fix everything up using ``pre-commit run --all-files``. You need to make the fixing commit yourself after that. Some of the hooks only run on CI by default, but you can invoke them by running with the ``--hook-stage manual`` argument. Troubleshooting the Installation -------------------------------- If you do not see that your Jupyter Server is not running on dev mode, it's possible that you are running other instances of Jupyter Server. You can try the following steps: 1. Uninstall all instances of the jupyter_server_terminals package. These include any installations you made using pip or conda 2. Run ``python3 -m pip install -e .`` in the jupyter_server_terminals repository to install jupyter_server_terminals from there 3. Launch with ``python3 -m jupyter_server --port 8989``, and check that the browser is pointing to ``localhost:8989`` (rather than the default 8888). You don't necessarily have to launch with port 8989, as long as you use a port that is neither the default nor in use, then it should be fine. 4. Verify the installation with the steps in the previous section. Running Tests ============= Install dependencies:: pip install -e .[test] pip install -e examples/simple # to test the examples To run the Python tests, use:: pytest jupyter_server_terminals Building the Docs ================= To build the documentation you'll need `Sphinx `_, `pandoc `_ and a few other packages. To install (and activate) a `conda environment`_ named ``jupyter_server_terminals_docs`` containing all the necessary packages (except pandoc), use:: conda env create -f docs/environment.yml conda activate jupyter_server_terminals_docs .. _conda environment: https://conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html#creating-an-environment-from-an-environment-yml-file If you want to install the necessary packages with ``pip`` instead:: pip install -r docs/doc-requirements.txt Once you have installed the required packages, you can build the docs with:: cd docs make html After that, the generated HTML files will be available at ``build/html/index.html``. You may view the docs in your browser. You can automatically check if all hyperlinks are still valid:: make linkcheck Windows users can find ``make.bat`` in the ``docs`` folder. You should also have a look at the `Project Jupyter Documentation Guide`__. __ https://jupyter.readthedocs.io/en/latest/contributing/content-contributor.html jupyter_server_terminals-0.5.3/LICENSE000066400000000000000000000030001457406420400176710ustar00rootroot00000000000000BSD 3-Clause License - Copyright (c) 2021-, Jupyter Development Team Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: All rights reserved. 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. jupyter_server_terminals-0.5.3/README.md000066400000000000000000000042131457406420400201520ustar00rootroot00000000000000# Jupyter Server Terminals [![Build Status](https://github.com/jupyter-server/jupyter_server_terminals/actions/workflows/test.yml/badge.svg?query=branch%3Amain++)](https://github.com/jupyter-server/jupyter_server_terminals/actions?query=branch%3Amain++) [![Documentation Status](https://readthedocs.org/projects/jupyter-server-terminals/badge/?version=latest)](http://jupyter-server-terminals.readthedocs.io/en/latest/?badge=latest) Jupyter Server Terminals is a Jupyter Server Extension providing support for terminals. ## Installation and Basic usage To install the latest release locally, make sure you have [pip installed](https://pip.readthedocs.io/en/stable/installing/) and run: ``` pip install jupyter_server_terminals ``` Jupyter Server Terminals currently supports Python>=3.6 on Linux, OSX and Windows. ### Testing See [CONTRIBUTING](./CONTRIBUTING.rst#running-tests). ## Contributing If you are interested in contributing to the project, see [CONTRIBUTING](./CONTRIBUTING.rst). ## 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. ``` jupyter_server_terminals-0.5.3/RELEASE.md000066400000000000000000000016621457406420400203020ustar00rootroot00000000000000# Making a Jupyter Server Release ## Using `jupyter_releaser` The recommended way to make a release is to use [`jupyter_releaser`](https://jupyter-releaser.readthedocs.io/en/latest/get_started/making_release_from_repo.html). ## Manual Release To create a manual release, perform the following steps: ### Set up ```bash pip install pipx git pull origin $(git branch --show-current) git clean -dffx ``` ### Update the version and apply the tag ```bash echo "Enter new version" read script_version pipx run hatch version ${script_version} git tag -a ${script_version} -m "${script_version}" ``` ### Build the artifacts ```bash rm -rf dist pipx run build . ``` ### Update the version back to dev ```bash echo "Enter dev version" read dev_version pipx run hatch version ${dev_version} git push origin $(git branch --show-current) ``` ### Publish the artifacts to pypi ```bash pipx run twine check dist/* pipx run twine upload dist/* ``` jupyter_server_terminals-0.5.3/docs/000077500000000000000000000000001457406420400176235ustar00rootroot00000000000000jupyter_server_terminals-0.5.3/docs/Makefile000066400000000000000000000011761457406420400212700ustar00rootroot00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line, and also # from the environment for the first two. SPHINXOPTS ?= SPHINXBUILD ?= sphinx-build SOURCEDIR = source 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) jupyter_server_terminals-0.5.3/docs/README.md000066400000000000000000000002711457406420400211020ustar00rootroot00000000000000# Jupyter Server Terminals Docs Sources Read [this page](https://jupyter-server-terminals.readthedocs.io/en/latest/contributors/contributing.html#building-the-docs) to build the docs. jupyter_server_terminals-0.5.3/docs/make.bat000066400000000000000000000014441457406420400212330ustar00rootroot00000000000000@ECHO OFF pushd %~dp0 REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set SOURCEDIR=source set BUILDDIR=build 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.https://www.sphinx-doc.org/ exit /b 1 ) %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% goto end :help %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% :end popd jupyter_server_terminals-0.5.3/docs/source/000077500000000000000000000000001457406420400211235ustar00rootroot00000000000000jupyter_server_terminals-0.5.3/docs/source/_static/000077500000000000000000000000001457406420400225515ustar00rootroot00000000000000jupyter_server_terminals-0.5.3/docs/source/_static/jupyter_server_logo.svg000066400000000000000000000243201457406420400274030ustar00rootroot00000000000000 image/svg+xml logo.svg logo.svg Created using Figma 0.90 server jupyter_server_terminals-0.5.3/docs/source/api.rst000066400000000000000000000010401457406420400224210ustar00rootroot00000000000000-------- REST API -------- The same Jupyter Server Terminals API spec, as found here, is available in an interactive form `here (on swagger's petstore) `__. The `OpenAPI Initiative`_ (fka Swaggerâ„¢) is a project used to describe and document RESTful APIs. .. openapi:: ../../jupyter_server_terminals/rest-api.yml :examples: .. _OpenAPI Initiative: https://www.openapis.org/ jupyter_server_terminals-0.5.3/docs/source/conf.py000066400000000000000000000271641457406420400224340ustar00rootroot00000000000000# Jupyter Server documentation build configuration file, created by # sphinx-quickstart on Mon Apr 13 09:51:11 2015. # # 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. import os import os.path as osp import shutil import sys from packaging.version import parse as parse_version HERE = osp.abspath(osp.dirname(__file__)) # 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. # DEBUG for RTD print("DEBUG:: sys.path") print("================") for item in sys.path: print(item) # add repo root to sys.path # here = root/docs/source here = os.path.abspath(os.path.dirname(__file__)) repo_root = os.path.dirname(os.path.dirname(here)) sys.path.insert(0, repo_root) print("repo_root") print("=====================") print(repo_root) # DEBUG for post insert on RTD print("DEBUG:: Post insert to sys.path") print("===============================") for item in sys.path: print(item) # -- 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 = [ "myst_parser", "sphinx.ext.autodoc", "sphinx.ext.doctest", "sphinx.ext.intersphinx", "sphinx.ext.autosummary", "sphinx.ext.mathjax", "sphinxcontrib_github_alt", "sphinxcontrib.openapi", "sphinxemoji.sphinxemoji", ] try: import enchant # type:ignore[import] # noqa: F401 extensions += ["sphinxcontrib.spelling"] except ImportError: pass myst_enable_extensions = ["html_image"] # 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'] source_suffix = [".rst", ".ipynb"] # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = "index" # General information about the project. project = "Jupyter Server Terminals" copyright = "2021, Jupyter Team, https://jupyter.org" author = "The Jupyter Server Team" # ghissue config github_project_url = "https://github.com/jupyter/jupyter_server_terminals" # 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. # __version__ = "0.3.1" # The short X.Y version. version_parsed = parse_version(__version__) version = f"{version_parsed.major}.{version_parsed.minor}" # type:ignore[union-attr] # The full version, including alpha/beta/rc tags. release = __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 for these cases. language = "en" # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = "default" # highlight_language = 'python3' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # # Add custom note for each doc page # rst_prolog = "" # rst_prolog += """ # .. important:: # This documentation covers Jupyter Server, an **early developer preview**, # and is not suitable for general usage yet. Features and implementation are # subject to change. # For production use cases, please use the stable notebook server in the # `Jupyter Notebook repo `_ # and `Jupyter Notebook documentation `_. # """ # -- 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_rtd_theme' html_theme = "pydata_sphinx_theme" html_logo = "_static/jupyter_server_logo.svg" # 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 = {"navigation_with_keys": False} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # 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". # NOTE: Sphinx's 'make html' builder will throw a warning about an unfound # _static directory. Do not remove or comment out html_static_path # since it is needed to properly generate _static in the build directory html_static_path = ["_static"] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr' # html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # Now only 'ja' uses this config value # html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. # html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. htmlhelp_basename = "JupyterServerTerminalsdoc" # -- Options for LaTeX output --------------------------------------------- # latex_elements = {} # 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, "JupyterServerTerminals.tex", "Jupyter Server Terminals Documentation", "https://jupyter.org", "manual", ), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- 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, "jupyterserverterminals", "Jupyter Server Terminals Documentation", [author], 1) ] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for link checks ---------------------------------------------- linkcheck_ignore = [r"http://127\.0\.0\.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, "JupyterServerTerminals", "Jupyter Server Terminals Documentation", author, "JupyterServerTerminals", "One line description of project.", "Miscellaneous", ), ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # texinfo_no_detailmenu = False intersphinx_mapping = { "jupyter": ("https://jupyter.readthedocs.io/en/latest/", None), "jupyter_server": ("https://jupyter-server.readthedocs.io/en/latest/", None), } spelling_lang = "en_US" spelling_word_list_filename = "spelling_wordlist.txt" def setup(app): dest = osp.join(HERE, "changelog.md") shutil.copy(osp.join(HERE, "..", "..", "CHANGELOG.md"), dest) jupyter_server_terminals-0.5.3/docs/source/contributors/000077500000000000000000000000001457406420400236605ustar00rootroot00000000000000jupyter_server_terminals-0.5.3/docs/source/contributors/contributing.rst000066400000000000000000000000721457406420400271200ustar00rootroot00000000000000.. highlight:: sh .. include:: ../../../CONTRIBUTING.rst jupyter_server_terminals-0.5.3/docs/source/index.rst000066400000000000000000000007741457406420400227740ustar00rootroot00000000000000.. jupyter_server_terminals documentation master file, created by sphinx-quickstart. You can adapt this file completely to your liking, but it should at least contain the root ``toctree``` directive. Welcome to Jupyter Server Terminals documentation! ================================================== .. toctree:: :maxdepth: 1 :caption: Contents: changelog api contributors/contributing Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` jupyter_server_terminals-0.5.3/jupyter-config/000077500000000000000000000000001457406420400216405ustar00rootroot00000000000000jupyter_server_terminals-0.5.3/jupyter-config/jupyter_server_terminals.json000066400000000000000000000001431457406420400276770ustar00rootroot00000000000000{ "ServerApp": { "jpserver_extensions": { "jupyter_server_terminals": true } } } jupyter_server_terminals-0.5.3/jupyter_server_terminals/000077500000000000000000000000001457406420400240415ustar00rootroot00000000000000jupyter_server_terminals-0.5.3/jupyter_server_terminals/__init__.py000066400000000000000000000012731457406420400261550ustar00rootroot00000000000000from typing import Any, Dict, List from ._version import __version__ # noqa:F401 try: from jupyter_server._version import version_info except ModuleNotFoundError: msg = "Jupyter Server must be installed to use this extension." raise ModuleNotFoundError(msg) from None if int(version_info[0]) < 2: # type:ignore[call-overload] msg = "Jupyter Server Terminals requires Jupyter Server 2.0+" raise RuntimeError(msg) from .app import TerminalsExtensionApp def _jupyter_server_extension_points() -> List[Dict[str, Any]]: # pragma: no cover return [ { "module": "jupyter_server_terminals.app", "app": TerminalsExtensionApp, }, ] jupyter_server_terminals-0.5.3/jupyter_server_terminals/_version.py000066400000000000000000000001071457406420400262350ustar00rootroot00000000000000"""Version info for jupyter_server_terminals.""" __version__ = "0.5.3" jupyter_server_terminals-0.5.3/jupyter_server_terminals/api_handlers.py000066400000000000000000000054361457406420400270540ustar00rootroot00000000000000"""API handlers for terminals.""" from __future__ import annotations import json from pathlib import Path from typing import Any from jupyter_server.auth.decorator import authorized from jupyter_server.base.handlers import APIHandler from tornado import web from .base import TerminalsMixin AUTH_RESOURCE = "terminals" class TerminalAPIHandler(APIHandler): """The base terminal handler.""" auth_resource = AUTH_RESOURCE class TerminalRootHandler(TerminalsMixin, TerminalAPIHandler): """The root termanal API handler.""" @web.authenticated @authorized def get(self) -> None: """Get the list of terminals.""" models = self.terminal_manager.list() self.finish(json.dumps(models)) @web.authenticated @authorized def post(self) -> None: """POST /terminals creates a new terminal and redirects to it""" data = self.get_json_body() or {} # if cwd is a relative path, it should be relative to the root_dir, # but if we pass it as relative, it will we be considered as relative to # the path jupyter_server was started in if "cwd" in data: cwd: Path | None = Path(data["cwd"]) assert cwd is not None if not cwd.resolve().exists(): cwd = Path(self.settings["server_root_dir"]).expanduser() / cwd if not cwd.resolve().exists(): cwd = None if cwd is None: server_root_dir = self.settings["server_root_dir"] self.log.debug( "Failed to find requested terminal cwd: %s\n" " It was not found within the server root neither: %s.", data.get("cwd"), server_root_dir, ) del data["cwd"] else: self.log.debug("Opening terminal in: %s", cwd.resolve()) data["cwd"] = str(cwd.resolve()) model = self.terminal_manager.create(**data) self.finish(json.dumps(model)) class TerminalHandler(TerminalsMixin, TerminalAPIHandler): """A handler for a specific terminal.""" SUPPORTED_METHODS = ("GET", "DELETE", "OPTIONS") # type:ignore[assignment] @web.authenticated @authorized def get(self, name: str) -> None: """Get a terminal by name.""" model = self.terminal_manager.get(name) self.finish(json.dumps(model)) @web.authenticated @authorized async def delete(self, name: str) -> None: """Remove a terminal by name.""" await self.terminal_manager.terminate(name, force=True) self.set_status(204) self.finish() default_handlers: list[tuple[str, type[Any]]] = [ (r"/api/terminals", TerminalRootHandler), (r"/api/terminals/(\w+)", TerminalHandler), ] jupyter_server_terminals-0.5.3/jupyter_server_terminals/app.py000066400000000000000000000121701457406420400251740ustar00rootroot00000000000000"""A terminals extension app.""" from __future__ import annotations import os import shlex import sys import typing as t from shutil import which from jupyter_core.utils import ensure_async from jupyter_server.extension.application import ExtensionApp from jupyter_server.transutils import trans from traitlets import Type from . import api_handlers, handlers from .terminalmanager import TerminalManager class TerminalsExtensionApp(ExtensionApp): """A terminals extension app.""" name = "jupyter_server_terminals" terminal_manager_class: type[TerminalManager] = Type( # type:ignore[assignment] default_value=TerminalManager, help="The terminal manager class to use." ).tag(config=True) # Since use of terminals is also a function of whether the terminado package is # available, this variable holds the "final indication" of whether terminal functionality # should be considered (particularly during shutdown/cleanup). It is enabled only # once both the terminals "service" can be initialized and terminals_enabled is True. # Note: this variable is slightly different from 'terminals_available' in the web settings # in that this variable *could* remain false if terminado is available, yet the terminal # service's initialization still fails. As a result, this variable holds the truth. terminals_available = False def initialize_settings(self) -> None: """Initialize settings.""" if not self.serverapp or not self.serverapp.terminals_enabled: self.settings.update({"terminals_available": False}) return self.initialize_configurables() self.settings.update( {"terminals_available": True, "terminal_manager": self.terminal_manager} ) def initialize_configurables(self) -> None: """Initialize configurables.""" default_shell = "powershell.exe" if os.name == "nt" else which("sh") assert self.serverapp is not None shell_override = self.serverapp.terminado_settings.get("shell_command") if isinstance(shell_override, str): shell_override = shlex.split(shell_override) shell = ( [os.environ.get("SHELL") or default_shell] if shell_override is None else shell_override ) # When the notebook server is not running in a terminal (e.g. when # it's launched by a JupyterHub spawner), it's likely that the user # environment hasn't been fully set up. In that case, run a login # shell to automatically source /etc/profile and the like, unless # the user has specifically set a preferred shell command. if os.name != "nt" and shell_override is None and not sys.stdout.isatty(): shell.append("-l") self.terminal_manager = self.terminal_manager_class( shell_command=shell, extra_env={ "JUPYTER_SERVER_ROOT": self.serverapp.root_dir, "JUPYTER_SERVER_URL": self.serverapp.connection_url, }, parent=self.serverapp, ) self.terminal_manager.log = self.serverapp.log def initialize_handlers(self) -> None: """Initialize handlers.""" if not self.serverapp: # Already set `terminals_available` as `False` in `initialize_settings` return if not self.serverapp.terminals_enabled: # webapp settings for backwards compat (used by nbclassic), #12 self.serverapp.web_app.settings["terminals_available"] = self.settings[ "terminals_available" ] return self.handlers.append( ( r"/terminals/websocket/(\w+)", handlers.TermSocket, {"term_manager": self.terminal_manager}, ) ) self.handlers.extend(api_handlers.default_handlers) assert self.serverapp is not None self.serverapp.web_app.settings["terminal_manager"] = self.terminal_manager self.serverapp.web_app.settings["terminals_available"] = self.settings[ "terminals_available" ] def current_activity(self) -> dict[str, t.Any] | None: """Get current activity info.""" if self.terminals_available: terminals = self.terminal_manager.terminals if terminals: return terminals return None async def cleanup_terminals(self) -> None: """Shutdown all terminals. The terminals will shutdown themselves when this process no longer exists, but explicit shutdown allows the TerminalManager to cleanup. """ if not self.terminals_available: return terminal_manager = self.terminal_manager n_terminals = len(terminal_manager.list()) terminal_msg = trans.ngettext( "Shutting down %d terminal", "Shutting down %d terminals", n_terminals ) self.log.info("%s %% %s", terminal_msg, n_terminals) await ensure_async(terminal_manager.terminate_all()) # type:ignore[arg-type] async def stop_extension(self) -> None: """Stop the extension.""" await self.cleanup_terminals() jupyter_server_terminals-0.5.3/jupyter_server_terminals/base.py000066400000000000000000000007451457406420400253330ustar00rootroot00000000000000"""Base classes.""" from __future__ import annotations from typing import TYPE_CHECKING from jupyter_server.extension.handler import ExtensionHandlerMixin if TYPE_CHECKING: from jupyter_server_terminals.terminalmanager import TerminalManager class TerminalsMixin(ExtensionHandlerMixin): """An extension mixin for terminals.""" @property def terminal_manager(self) -> TerminalManager: return self.settings["terminal_manager"] # type:ignore[no-any-return] jupyter_server_terminals-0.5.3/jupyter_server_terminals/handlers.py000066400000000000000000000055071457406420400262220ustar00rootroot00000000000000"""Tornado handlers for the terminal emulator.""" # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. from __future__ import annotations import typing as t from jupyter_core.utils import ensure_async from jupyter_server._tz import utcnow from jupyter_server.auth.utils import warn_disabled_authorization from jupyter_server.base.handlers import JupyterHandler from jupyter_server.base.websocket import WebSocketMixin from terminado.management import NamedTermManager from terminado.websocket import TermSocket as BaseTermSocket from tornado import web from .base import TerminalsMixin AUTH_RESOURCE = "terminals" class TermSocket(TerminalsMixin, WebSocketMixin, JupyterHandler, BaseTermSocket): """A terminal websocket.""" auth_resource = AUTH_RESOURCE def initialize( # type:ignore[override] self, name: str, term_manager: NamedTermManager, **kwargs: t.Any ) -> None: """Initialize the socket.""" BaseTermSocket.initialize(self, term_manager, **kwargs) TerminalsMixin.initialize(self, name) def origin_check(self, origin: t.Any = None) -> bool: """Terminado adds redundant origin_check Tornado already calls check_origin, so don't do anything here. """ return True async def get(self, *args: t.Any, **kwargs: t.Any) -> None: """Get the terminal socket.""" user = self.current_user if not user: raise web.HTTPError(403) # authorize the user. if self.authorizer is None: # Warn if an authorizer is unavailable. warn_disabled_authorization() # type:ignore[unreachable] elif not self.authorizer.is_authorized(self, user, "execute", self.auth_resource): raise web.HTTPError(403) if args[0] not in self.term_manager.terminals: # type:ignore[attr-defined] raise web.HTTPError(404) resp = super().get(*args, **kwargs) if resp is not None: await ensure_async(resp) # type:ignore[arg-type] async def on_message(self, message: t.Any) -> None: # type:ignore[override] """Handle a socket message.""" await ensure_async(super().on_message(message)) # type:ignore[arg-type] self._update_activity() def write_message(self, message: t.Any, binary: bool = False) -> None: # type:ignore[override] """Write a message to the socket.""" super().write_message(message, binary=binary) self._update_activity() def _update_activity(self) -> None: self.application.settings["terminal_last_activity"] = utcnow() # terminal may not be around on deletion/cull if self.term_name in self.terminal_manager.terminals: self.terminal_manager.terminals[self.term_name].last_activity = utcnow() # type:ignore[attr-defined] jupyter_server_terminals-0.5.3/jupyter_server_terminals/py.typed000066400000000000000000000000001457406420400255260ustar00rootroot00000000000000jupyter_server_terminals-0.5.3/jupyter_server_terminals/rest-api.yml000066400000000000000000000057451457406420400263230ustar00rootroot00000000000000openapi: 3.0.1 info: title: Jupyter Server Terminals API description: Terminals API contact: name: Jupyter Project url: https://jupyter.org version: "1" servers: - url: / paths: /api/terminals: get: tags: - terminals summary: Get available terminals responses: 200: description: A list of all available terminal ids. content: application/json: schema: type: array items: $ref: "#/components/schemas/Terminal" 403: description: Forbidden to access content: {} 404: description: Not found content: {} post: tags: - terminals summary: Create a new terminal responses: 200: description: Successfully created a new terminal content: application/json: schema: $ref: "#/components/schemas/Terminal" 403: description: Forbidden to access content: {} 404: description: Not found content: {} /api/terminals/{terminal_id}: get: tags: - terminals summary: Get a terminal session corresponding to an id. parameters: - name: terminal_id in: path description: ID of terminal session required: true schema: type: string responses: 200: description: Terminal session with given id content: application/json: schema: $ref: "#/components/schemas/Terminal" 403: description: Forbidden to access content: {} 404: description: Not found content: {} delete: tags: - terminals summary: Delete a terminal session corresponding to an id. parameters: - name: terminal_id in: path description: ID of terminal session required: true schema: type: string responses: 204: description: Successfully deleted terminal session content: {} 403: description: Forbidden to access content: {} 404: description: Not found content: {} components: schemas: Terminal: required: - name type: object properties: name: type: string description: name of terminal last_activity: type: string description: | ISO 8601 timestamp for the last-seen activity on this terminal. Use this to identify which terminals have been inactive since a given time. Timestamps will be UTC, indicated 'Z' suffix. description: A Terminal object parameters: terminal_id: name: terminal_id in: path description: ID of terminal session required: true schema: type: string jupyter_server_terminals-0.5.3/jupyter_server_terminals/terminalmanager.py000066400000000000000000000153541457406420400275710ustar00rootroot00000000000000"""A MultiTerminalManager for use in the notebook webserver - raises HTTPErrors - creates REST API models """ # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. from __future__ import annotations import typing as t from datetime import timedelta from jupyter_server._tz import isoformat, utcnow from jupyter_server.prometheus import metrics from terminado.management import NamedTermManager, PtyWithClients from tornado import web from tornado.ioloop import IOLoop, PeriodicCallback from traitlets import Integer from traitlets.config import LoggingConfigurable RUNNING_TOTAL = metrics.TERMINAL_CURRENTLY_RUNNING_TOTAL MODEL = t.Dict[str, t.Any] class TerminalManager(LoggingConfigurable, NamedTermManager): # type:ignore[misc] """A MultiTerminalManager for use in the notebook webserver""" _culler_callback = None _initialized_culler = False cull_inactive_timeout = Integer( 0, config=True, help="""Timeout (in seconds) in which a terminal has been inactive and ready to be culled. Values of 0 or lower disable culling.""", ) cull_interval_default = 300 # 5 minutes cull_interval = Integer( cull_interval_default, config=True, help="""The interval (in seconds) on which to check for terminals exceeding the inactive timeout value.""", ) # ------------------------------------------------------------------------- # Methods for managing terminals # ------------------------------------------------------------------------- def create(self, **kwargs: t.Any) -> MODEL: """Create a new terminal.""" name, term = self.new_named_terminal(**kwargs) # Monkey-patch last-activity, similar to kernels. Should we need # more functionality per terminal, we can look into possible sub- # classing or containment then. term.last_activity = utcnow() # type:ignore[attr-defined] model = self.get_terminal_model(name) # Increase the metric by one because a new terminal was created RUNNING_TOTAL.inc() # Ensure culler is initialized self._initialize_culler() return model def get(self, name: str) -> MODEL: """Get terminal 'name'.""" return self.get_terminal_model(name) def list(self) -> list[MODEL]: """Get a list of all running terminals.""" models = [self.get_terminal_model(name) for name in self.terminals] # Update the metric below to the length of the list 'terms' RUNNING_TOTAL.set(len(models)) return models async def terminate(self, name: str, force: bool = False) -> None: """Terminate terminal 'name'.""" self._check_terminal(name) await super().terminate(name, force=force) # Decrease the metric below by one # because a terminal has been shutdown RUNNING_TOTAL.dec() async def terminate_all(self) -> None: """Terminate all terminals.""" terms = list(self.terminals) for term in terms: await self.terminate(term, force=True) def get_terminal_model(self, name: str) -> MODEL: """Return a JSON-safe dict representing a terminal. For use in representing terminals in the JSON APIs. """ self._check_terminal(name) term = self.terminals[name] return { "name": name, "last_activity": isoformat(term.last_activity), # type:ignore[attr-defined] } def _check_terminal(self, name: str) -> None: """Check a that terminal 'name' exists and raise 404 if not.""" if name not in self.terminals: raise web.HTTPError(404, "Terminal not found: %s" % name) def _initialize_culler(self) -> None: """Start culler if 'cull_inactive_timeout' is greater than zero. Regardless of that value, set flag that we've been here. """ if not self._initialized_culler and self.cull_inactive_timeout > 0: # noqa: SIM102 if self._culler_callback is None: _ = IOLoop.current() if self.cull_interval <= 0: # handle case where user set invalid value self.log.warning( "Invalid value for 'cull_interval' detected (%s) - using default value (%s).", self.cull_interval, self.cull_interval_default, ) self.cull_interval = self.cull_interval_default self._culler_callback = PeriodicCallback( self._cull_terminals, 1000 * self.cull_interval ) self.log.info( "Culling terminals with inactivity > %s seconds at %s second intervals ...", self.cull_inactive_timeout, self.cull_interval, ) self._culler_callback.start() self._initialized_culler = True async def _cull_terminals(self) -> None: self.log.debug( "Polling every %s seconds for terminals inactive for > %s seconds...", self.cull_interval, self.cull_inactive_timeout, ) # Create a separate list of terminals to avoid conflicting updates while iterating for name in list(self.terminals): try: await self._cull_inactive_terminal(name) except Exception as e: self.log.exception( "The following exception was encountered while checking the " "activity of terminal %s: %s", name, e, ) async def _cull_inactive_terminal(self, name: str) -> None: try: term = self.terminals[name] except KeyError: return # KeyErrors are somewhat expected since the terminal can be terminated as the culling check is made. self.log.debug("name=%s, last_activity=%s", name, term.last_activity) # type:ignore[attr-defined] if hasattr(term, "last_activity"): dt_now = utcnow() dt_inactive = dt_now - term.last_activity # Compute idle properties is_time = dt_inactive > timedelta(seconds=self.cull_inactive_timeout) # Cull the kernel if all three criteria are met if is_time: inactivity = int(dt_inactive.total_seconds()) self.log.warning( "Culling terminal '%s' due to %s seconds of inactivity.", name, inactivity ) await self.terminate(name, force=True) def pre_pty_read_hook(self, ptywclients: PtyWithClients) -> None: """The pre-pty read hook.""" ptywclients.last_activity = utcnow() # type:ignore[attr-defined] jupyter_server_terminals-0.5.3/pyproject.toml000066400000000000000000000113761457406420400216170ustar00rootroot00000000000000[build-system] requires = ["hatchling>=1.5"] build-backend = "hatchling.build" [project] name = "jupyter_server_terminals" readme = "README.md" dynamic = ["version"] license = { file = "LICENSE" } description = "A Jupyter Server Extension Providing Terminals." keywords = ["ipython", "jupyter"] classifiers = [ "Intended Audience :: Developers", "Intended Audience :: System Administrators", "Intended Audience :: Science/Research", "License :: OSI Approved :: BSD License", "Programming Language :: Python", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11" ] requires-python = ">=3.8" dependencies = [ "pywinpty>=2.0.3;os_name=='nt'", "terminado>=0.8.3", ] [[project.authors]] name = "Jupyter Development Team" email = "jupyter@googlegroups.com" [project.urls] Homepage = "https://jupyter.org" [project.optional-dependencies] test = [ "jupyter_server>=2.0.0", "pytest-jupyter[server]>=0.5.3", "pytest>=7.0", "pytest-timeout", ] docs = [ "jinja2", "jupyter_server", "mistune<4.0", # https://github.com/jupyter/nbconvert/issues/1685" "myst-parser", "nbformat", "packaging", "tornado", "pydata_sphinx_theme", "sphinxcontrib-openapi", "sphinxcontrib_github_alt", "sphinxemoji", "sphinxcontrib-spelling", ] [tool.hatch.version] path = "jupyter_server_terminals/_version.py" [tool.hatch.build.targets.wheel.shared-data] "jupyter-config" = "etc/jupyter/jupyter_server_config.d" [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 jupyter_server_terminals --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/" ] timeout = 300 # Restore this setting to debug failures # timeout_method = "thread" filterwarnings = [ "error", # from tornado "ignore:unclosed 100 characters) "SIM105", # Use `contextlib.suppress(...)` "T201", # `print` found "S101", # Use of `assert` detected ] unfixable = [ # Don't touch print statements "T201", # Don't touch noqa lines "RUF100", ] [tool.ruff.lint.per-file-ignores] # B011: Do not call assert False since python -O removes these calls # F841 local variable 'foo' is assigned to but never used # S101 Use of `assert` detected "tests/*" = ["B011", "F841"] "docs/*" = ["PTH"] [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 = ["GH102"] jupyter_server_terminals-0.5.3/tests/000077500000000000000000000000001457406420400200355ustar00rootroot00000000000000jupyter_server_terminals-0.5.3/tests/__init__.py000066400000000000000000000000001457406420400221340ustar00rootroot00000000000000jupyter_server_terminals-0.5.3/tests/conftest.py000066400000000000000000000000631457406420400222330ustar00rootroot00000000000000pytest_plugins = ["pytest_jupyter.jupyter_server"] jupyter_server_terminals-0.5.3/tests/test_auth.py000066400000000000000000000104451457406420400224130ustar00rootroot00000000000000"""Tests for authorization""" import asyncio from typing import Dict import pytest from jupyter_server.auth.authorizer import Authorizer from jupyter_server.auth.utils import HTTP_METHOD_TO_AUTH_ACTION, match_url_to_resource from tornado.httpclient import HTTPClientError from tornado.websocket import WebSocketHandler from traitlets.config.loader import Config class AuthorizerforTesting(Authorizer): # Set these class attributes from within a test # to verify that they match the arguments passed # by the REST API. permissions: Dict[str, str] = {} # noqa: RUF012 def normalize_url(self, path): """Drop the base URL and make sure path leads with a /""" base_url = self.parent.base_url # Remove base_url if path.startswith(base_url): path = path[len(base_url) :] # Make sure path starts with / if not path.startswith("/"): path = "/" + path return path def is_authorized(self, handler, user, action, resource): # Parse Request method = "WEBSOCKET" if isinstance(handler, WebSocketHandler) else handler.request.method url = self.normalize_url(handler.request.path) # Map request parts to expected action and resource. expected_action = HTTP_METHOD_TO_AUTH_ACTION[method] expected_resource = match_url_to_resource(url) # Assert that authorization layer returns the # correct action + resource. assert action == expected_action assert resource == expected_resource # Now, actually apply the authorization layer. return all( [ action in self.permissions.get("actions", []), resource in self.permissions.get("resources", []), ] ) @pytest.fixture() def jp_server_config(): return Config( { "ServerApp": { "jpserver_extensions": {"jupyter_server_terminals": True}, "authorizer_class": AuthorizerforTesting, } } ) @pytest.fixture() def send_request(jp_fetch, jp_ws_fetch): """Send to Jupyter Server and return response code.""" async def _(url, **fetch_kwargs): fetch = jp_ws_fetch if url.endswith("channels") or "/websocket/" in url else jp_fetch try: r = await fetch(url, **fetch_kwargs, allow_nonstandard_methods=True) code = r.code except HTTPClientError as err: code = err.code else: if fetch is jp_ws_fetch: r.close() print(code, url, fetch_kwargs) return code return _ HTTP_REQUESTS = [ { "method": "POST", "url": "/api/terminals", "body": "", }, { "method": "GET", "url": "/api/terminals", }, { "method": "GET", "url": "/terminals/websocket/{term_name}", }, { "method": "DELETE", "url": "/api/terminals/{term_name}", }, ] HTTP_REQUESTS_PARAMETRIZED = [(req["method"], req["url"], req.get("body")) for req in HTTP_REQUESTS] # -------- Test scenarios ----------- @pytest.mark.parametrize("method, url, body", HTTP_REQUESTS_PARAMETRIZED) # noqa: PT006 @pytest.mark.parametrize("allowed", (True, False)) # noqa: PT007 async def test_authorized_requests( request, io_loop, send_request, jp_serverapp, method, url, body, allowed, ): term_manager = jp_serverapp.web_app.settings["terminal_manager"] request.addfinalizer(lambda: io_loop.run_sync(term_manager.terminate_all)) term_model = term_manager.create() term_name = term_model["name"] url = url.format(term_name=term_name) if allowed: # Create a server with full permissions permissions = { "actions": ["read", "write", "execute"], "resources": [ "terminals", ], } expected_codes = {200, 201, 204, None} # Websockets don't return a code else: permissions = {"actions": [], "resources": []} expected_codes = {403} jp_serverapp.authorizer.permissions = permissions while True: code = await send_request(url, body=body, method=method) if code == 404: await asyncio.sleep(1) continue assert code in expected_codes break jupyter_server_terminals-0.5.3/tests/test_disable_app.py000066400000000000000000000007111457406420400237100ustar00rootroot00000000000000import pytest from traitlets.config.loader import Config @pytest.fixture() def jp_server_config(): return Config({"ServerApp": {"terminals_enabled": False}}) async def test_not_enabled(jp_configurable_serverapp): assert jp_configurable_serverapp().terminals_enabled is False assert jp_configurable_serverapp().web_app.settings["terminals_available"] is False assert "terminal_manager" not in jp_configurable_serverapp().web_app.settings jupyter_server_terminals-0.5.3/tests/test_terminal.py000066400000000000000000000161141457406420400232640ustar00rootroot00000000000000import asyncio import json import os import shutil import sys from pathlib import Path import pytest from tornado.httpclient import HTTPClientError from traitlets.config.loader import Config @pytest.fixture() def terminal_path(tmp_path): subdir = tmp_path.joinpath("terminal_path") subdir.mkdir() yield subdir shutil.rmtree(str(subdir), ignore_errors=True) @pytest.fixture() def terminal_root_dir(jp_root_dir): subdir = jp_root_dir.joinpath("terminal_path") subdir.mkdir() yield subdir shutil.rmtree(str(subdir), ignore_errors=True) CULL_TIMEOUT = 10 CULL_INTERVAL = 3 @pytest.fixture() def jp_server_config(): return Config( { "ServerApp": { "TerminalManager": { "cull_inactive_timeout": CULL_TIMEOUT, "cull_interval": CULL_INTERVAL, }, "jpserver_extensions": {"jupyter_server_terminals": True}, } } ) async def test_no_terminals(jp_fetch): resp_list = await jp_fetch( "api", "terminals", method="GET", allow_nonstandard_methods=True, ) data = json.loads(resp_list.body.decode()) assert len(data) == 0 async def test_terminal_create(jp_fetch): resp = await jp_fetch( "api", "terminals", method="POST", allow_nonstandard_methods=True, ) term = json.loads(resp.body.decode()) assert term["name"] == "1" resp_list = await jp_fetch( "api", "terminals", method="GET", allow_nonstandard_methods=True, ) data = json.loads(resp_list.body.decode()) assert len(data) == 1 del data[0]["last_activity"] del term["last_activity"] assert data[0] == term async def test_terminal_create_with_kwargs(jp_fetch, terminal_path): resp_create = await jp_fetch( "api", "terminals", method="POST", body=json.dumps({"cwd": str(terminal_path)}), allow_nonstandard_methods=True, ) data = json.loads(resp_create.body.decode()) term_name = data["name"] resp_get = await jp_fetch( "api", "terminals", term_name, method="GET", allow_nonstandard_methods=True, ) data = json.loads(resp_get.body.decode()) assert data["name"] == term_name async def test_terminal_create_with_cwd(jp_fetch, jp_ws_fetch, terminal_path): resp = await jp_fetch( "api", "terminals", method="POST", body=json.dumps({"cwd": str(terminal_path)}), allow_nonstandard_methods=True, ) data = json.loads(resp.body.decode()) term_name = data["name"] while True: try: ws = await jp_ws_fetch("terminals", "websocket", term_name) break except HTTPClientError as e: if e.code != 404: raise await asyncio.sleep(1) ws.write_message(json.dumps(["stdin", "pwd\r\n"])) message_stdout = "" while True: try: message = await asyncio.wait_for(ws.read_message(), timeout=5.0) except asyncio.TimeoutError: break message = json.loads(message) if message[0] == "stdout": message_stdout += message[1] ws.close() assert Path(terminal_path).name in message_stdout async def test_terminal_create_with_relative_cwd( jp_fetch, jp_ws_fetch, jp_root_dir, terminal_root_dir ): resp = await jp_fetch( "api", "terminals", method="POST", body=json.dumps({"cwd": str(terminal_root_dir.relative_to(jp_root_dir))}), allow_nonstandard_methods=True, ) data = json.loads(resp.body.decode()) term_name = data["name"] while True: try: ws = await jp_ws_fetch("terminals", "websocket", term_name) break except HTTPClientError as e: if e.code != 404: raise await asyncio.sleep(1) ws.write_message(json.dumps(["stdin", "pwd\r\n"])) message_stdout = "" while True: try: message = await asyncio.wait_for(ws.read_message(), timeout=5.0) except asyncio.TimeoutError: break message = json.loads(message) if message[0] == "stdout": message_stdout += message[1] ws.close() expected = terminal_root_dir.name if sys.platform == "win32" else str(terminal_root_dir) assert expected in message_stdout async def test_terminal_create_with_bad_cwd(jp_fetch, jp_ws_fetch): non_existing_path = "/tmp/path/to/nowhere" # noqa: S108 resp = await jp_fetch( "api", "terminals", method="POST", body=json.dumps({"cwd": non_existing_path}), allow_nonstandard_methods=True, ) data = json.loads(resp.body.decode()) term_name = data["name"] while True: try: ws = await jp_ws_fetch("terminals", "websocket", term_name) break except HTTPClientError as e: if e.code != 404: raise await asyncio.sleep(1) ws.write_message(json.dumps(["stdin", "pwd\r\n"])) message_stdout = "" while True: try: message = await asyncio.wait_for(ws.read_message(), timeout=5.0) except asyncio.TimeoutError: break message = json.loads(message) if message[0] == "stdout": message_stdout += message[1] ws.close() assert non_existing_path not in message_stdout async def test_app_config(jp_configurable_serverapp): assert jp_configurable_serverapp().terminals_enabled is True assert jp_configurable_serverapp().web_app.settings["terminals_available"] is True assert jp_configurable_serverapp().web_app.settings["terminal_manager"] async def test_culling_config(jp_configurable_serverapp): terminal_mgr_config = jp_configurable_serverapp().config.ServerApp.TerminalManager assert terminal_mgr_config.cull_inactive_timeout == CULL_TIMEOUT assert terminal_mgr_config.cull_interval == CULL_INTERVAL terminal_mgr_settings = jp_configurable_serverapp().web_app.settings["terminal_manager"] assert terminal_mgr_settings.cull_inactive_timeout == CULL_TIMEOUT assert terminal_mgr_settings.cull_interval == CULL_INTERVAL @pytest.mark.skipif(os.name == "nt", reason="Not currently working on Windows") async def test_culling(jp_fetch): # POST request resp = await jp_fetch( "api", "terminals", method="POST", allow_nonstandard_methods=True, ) term = json.loads(resp.body.decode()) term_1 = term["name"] last_activity = term["last_activity"] culled = False for _ in range(CULL_TIMEOUT + CULL_INTERVAL * 2): try: resp = await jp_fetch( "api", "terminals", term_1, method="GET", allow_nonstandard_methods=True, ) except HTTPClientError as e: assert e.code == 404 # noqa: PT017 culled = True break else: await asyncio.sleep(1) assert culled