diff --git a/.github/dependabot.yml b/.github/dependabot.yml index cbd920f..354ae8a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,8 +3,16 @@ updates: - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "weekly" + interval: "monthly" + groups: + actions: + patterns: + - "*" - package-ecosystem: "pip" directory: "/" schedule: - interval: "weekly" + interval: "monthly" + groups: + pip: + patterns: + - "*" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d82a3d0..a5f59b9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,8 +4,6 @@ on: push: branches: ["main"] pull_request: - schedule: - - cron: "0 0 * * *" defaults: run: @@ -18,54 +16,78 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] - python-version: [ '3.8', '3.9', '3.10', "3.11" ] + python-version: ["3.9", "3.13"] steps: - - name: Checkout - uses: actions/checkout@v2 + - name: Checkout + uses: actions/checkout@v4 - - name: Base Setup - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 + - name: Base Setup + uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 - - name: Install dependencies - run: python -m pip install -U jupyter_server + - name: Install the extension + run: | + python -m pip install ".[test]" - - name: Build the extension - run: | - python -m pip install . - jupyter server extension list 2>&1 | grep -ie "jupyter_nbmodel_client.*OK" + - name: Test the extension + run: | + pytest . - pip install build - python -m build --sdist - cp dist/*.tar.gz my_server_extension.tar.gz - pip uninstall -y "jupyter_nbmodel_client" jupyter_server - rm -rf "jupyter_nbmodel_client" + - name: Build the extension + run: | + pip install build + python -m build --sdist + cp dist/*.tar.gz my_server_extension.tar.gz + pip uninstall -y "jupyter_kernel_client" + rm -rf "jupyter_kernel_client" - - uses: actions/upload-artifact@v2 - with: - name: my_server_extension-sdist - path: my_server_extension.tar.gz + - uses: actions/upload-artifact@v4 + with: + name: my_server_extension-sdist + path: my_server_extension.tar.gz check_links: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 - uses: jupyterlab/maintainer-tools/.github/actions/check-links@v1 test_lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 - name: Run Linters run: | bash ./.github/workflows/lint.sh + test_sdist: + needs: build + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + architecture: "x64" + - uses: actions/download-artifact@v4 + with: + name: my_server_extension-sdist + - name: Install and Test + run: | + pip install my_server_extension.tar.gz + pip list 2>&1 | grep -ie "jupyter_kernel_client" + python -c "import jupyter_kernel_client" + check_release: + needs: test_sdist runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Base Setup uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 - name: Install Dependencies @@ -76,28 +98,7 @@ jobs: with: token: ${{ secrets.GITHUB_TOKEN }} - name: Upload Distributions - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: - name: jupyter_nbmodel_client-releaser-dist-${{ github.run_number }} + name: jupyter_kernel_client-releaser-dist-${{ github.run_number }} path: .jupyter_releaser_checkout/dist - - test_sdist: - needs: build - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Install Python - uses: actions/setup-python@v2 - with: - python-version: '3.8' - architecture: 'x64' - - uses: actions/download-artifact@v2 - with: - name: my_server_extension-sdist - - name: Install and Test - run: | - pip install my_server_extension.tar.gz - pip install jupyter_server - jupyter server extension list 2>&1 | grep -ie "jupyter_nbmodel_client.*OK" diff --git a/.github/workflows/fix-license-header.yml b/.github/workflows/fix-license-header.yml new file mode 100644 index 0000000..10386f6 --- /dev/null +++ b/.github/workflows/fix-license-header.yml @@ -0,0 +1,62 @@ +name: Fix License Headers + +on: + pull_request_target: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + header-license-fix: + runs-on: ubuntu-latest + + permissions: + contents: write + pull-requests: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Checkout the branch from the PR that triggered the job + run: gh pr checkout ${{ github.event.pull_request.number }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Fix License Header + # pin to include https://github.com/apache/skywalking-eyes/pull/168 + uses: apache/skywalking-eyes/header@e19b828cea6a6027cceae78f05d81317347d21be + with: + mode: fix + + - name: List files changed + id: files-changed + shell: bash -l {0} + run: | + set -ex + export CHANGES=$(git status --porcelain | tee /tmp/modified.log | wc -l) + cat /tmp/modified.log + + echo "N_CHANGES=${CHANGES}" >> $GITHUB_OUTPUT + + git diff + + - name: Commit any changes + if: steps.files-changed.outputs.N_CHANGES != '0' + shell: bash -l {0} + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + git pull --no-tags + + git add * + git commit -m "Automatic application of license header" + + git config push.default upstream + git push + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/lint.sh b/.github/workflows/lint.sh index 26ff0d4..a4970c3 100644 --- a/.github/workflows/lint.sh +++ b/.github/workflows/lint.sh @@ -1,7 +1,6 @@ #!/usr/bin/env bash -pip install -e ".[test,lint]" +pip install -e ".[lint, typing]" mypy --install-types --non-interactive . -ruff . -black --check --diff . +ruff check . mdformat --check *.md pipx run 'validate-pyproject[all]' pyproject.toml diff --git a/.gitignore b/.gitignore index ecb2472..a7e3e72 100644 --- a/.gitignore +++ b/.gitignore @@ -93,6 +93,9 @@ celerybeat-schedule .dmypy.json dmypy.json +# ruff +.ruff_cache + # Pyre type checker .pyre/ diff --git a/.licenserc.yaml b/.licenserc.yaml new file mode 100644 index 0000000..0482a68 --- /dev/null +++ b/.licenserc.yaml @@ -0,0 +1,16 @@ +header: + license: + spdx-id: BSD-3-Clause + copyright-owner: Datalayer, Inc. + copyright-year: 2023-2024 + software-name: jupyter-kernel-client + + paths-ignore: + - '**/*.ipynb' + - '**/*.json' + - '**/*.yaml' + - '**/*.yml' + - '**/.*' + - 'LICENSE' + + comment: on-failure \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bdaad89..60cdea0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,7 @@ ci: repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v5.0.0 hooks: - id: end-of-file-fixer - id: check-case-conflict @@ -19,24 +19,20 @@ repos: - id: trailing-whitespace - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.19.2 + rev: 0.29.4 hooks: - id: check-github-workflows - repo: https://github.com/executablebooks/mdformat - rev: 0.7.16 + rev: 0.7.19 hooks: - id: mdformat additional_dependencies: [mdformat-gfm, mdformat-frontmatter, mdformat-footnote] - - repo: https://github.com/psf/black - rev: 22.10.0 - hooks: - - id: black - - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.165 + rev: v0.8.0 hooks: - id: ruff args: ["--fix"] + - id: ruff-format diff --git a/README.md b/README.md index 3bbbbd1..e6312c8 100644 --- a/README.md +++ b/README.md @@ -42,22 +42,7 @@ jupyter server extension list # Change directory to the jupyter_nbmodel_client directory # Install package in development mode - will automatically enable # The server extension. -pip install -e . -``` - - -You can watch the source directory and run your Jupyter Server-based application at the same time in different terminals to watch for changes in the extension's source and automatically rebuild the extension. For example, -when running JupyterLab: - -```bash -jupyter lab --autoreload -``` - -If your extension does not depend a particular frontend, you can run the -server directly: - -```bash -jupyter server --autoreload +pip install -e ".[test,lint,typing]" ``` ### Running Tests @@ -72,12 +57,6 @@ To run the python tests, use: ```bash pytest - -# To test a specific file -pytest jupyter_nbmodel_client/tests/test_handlers.py - -# To run a specific test -pytest jupyter_nbmodel_client/tests/test_handlers.py -k "test_get" ``` ### Development uninstall diff --git a/RELEASE.md b/RELEASE.md index 87735b5..15e0877 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -6,8 +6,8 @@ The extension can be published to `PyPI` manually or using the [Jupyter Releaser ### Python package -This extension can be distributed as Python -packages. All of the Python +This repository can be distributed as Python +package. All of the Python packaging instructions in the `pyproject.toml` file to wrap your extension in a Python package. Before generating a package, we first need to install `build`. @@ -21,38 +21,29 @@ To create a Python source package (`.tar.gz`) and the binary package (`.whl`) in python -m build ``` -> `python setup.py sdist bdist_wheel` is deprecated and will not work for this package. - Then to upload the package to PyPI, do: ```bash twine upload dist/* ``` -### NPM package - -To publish the frontend part of the extension as a NPM package, do: - -```bash -npm login -npm publish --access public -``` - ## Automated releases with the Jupyter Releaser -The extension repository should already be compatible with the Jupyter Releaser. - -Check out the [workflow documentation](https://github.com/jupyter-server/jupyter_releaser#typical-workflow) for more information. +> [!NOTE] +> The extension repository is compatible with the Jupyter Releaser. But +> the GitHub repository and PyPI may need to be properly set up. Please +> follow the instructions of the Jupyter Releaser [checklist](https://jupyter-releaser.readthedocs.io/en/latest/how_to_guides/convert_repo_from_repo.html). Here is a summary of the steps to cut a new release: -- Fork the [`jupyter-releaser` repo](https://github.com/jupyter-server/jupyter_releaser) -- Add `ADMIN_GITHUB_TOKEN`, `PYPI_TOKEN` and `NPM_TOKEN` to the Github Secrets in the fork - Go to the Actions panel -- Run the "Draft Changelog" workflow -- Merge the Changelog PR -- Run the "Draft Release" workflow -- Run the "Publish Release" workflow +- Run the "Step 1: Prep Release" workflow +- Check the draft changelog +- Run the "Step 2: Publish Release" workflow + +> [!NOTE] +> Check out the [workflow documentation](https://jupyter-releaser.readthedocs.io/en/latest/get_started/making_release_from_repo.html) +> for more information. ## Publishing to `conda-forge` diff --git a/jupyter-config/jupyter_server_config.d/jupyter_nbmodel_client.json b/jupyter-config/jupyter_server_config.d/jupyter_nbmodel_client.json deleted file mode 100644 index b6b81bc..0000000 --- a/jupyter-config/jupyter_server_config.d/jupyter_nbmodel_client.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "ServerApp": { - "jpserver_extensions": { - "jupyter_nbmodel_client": true - } - } -} diff --git a/jupyter_nbmodel_client/__init__.py b/jupyter_nbmodel_client/__init__.py index bcc2e96..5d0696b 100644 --- a/jupyter_nbmodel_client/__init__.py +++ b/jupyter_nbmodel_client/__init__.py @@ -1,10 +1,3 @@ """Client to interact with Jupyter notebook model.""" -from .extension import Extension -__version__ = "0.1.0" - -def _jupyter_server_extension_points(): - return [{ - "module": "jupyter_nbmodel_client", - "app": Extension - }] +__version__ = "0.1.0" diff --git a/jupyter_nbmodel_client/extension.py b/jupyter_nbmodel_client/extension.py deleted file mode 100644 index 9aa1865..0000000 --- a/jupyter_nbmodel_client/extension.py +++ /dev/null @@ -1,21 +0,0 @@ -from traitlets import Unicode - -from jupyter_server.extension.application import ExtensionApp -from .handlers import PingHandler - - -class Extension(ExtensionApp): - - name = "jupyter_nbmodel_client" - handlers = [ - ("jupyter-nbmodel-client/ping", PingHandler) - ] - - # Example of a configurable trait. This is meant to be replaced - # with configurable traits for this extension. - ping_response = Unicode(default_value="pong").tag(config=True) - - def initialize_settings(self): - self.settings.update({ - "ping_response": self.ping_response - }) diff --git a/jupyter_nbmodel_client/handlers.py b/jupyter_nbmodel_client/handlers.py deleted file mode 100644 index 8579980..0000000 --- a/jupyter_nbmodel_client/handlers.py +++ /dev/null @@ -1,20 +0,0 @@ -import json - -from jupyter_server.extension.handler import ExtensionHandlerMixin -from jupyter_server.base.handlers import APIHandler -import tornado - - -class PingHandler(ExtensionHandlerMixin, APIHandler): - # The following decorator should be present on all verb methods (head, get, post, - # patch, put, delete, options) to ensure only authorized user can request the - # Jupyter server - @property - def ping_response(self): - return self.settings["ping_response"] - - @tornado.web.authenticated - def get(self): - self.finish(json.dumps({ - "ping_response": self.ping_response - })) diff --git a/pyproject.toml b/pyproject.toml index c118537..3bf5d90 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,41 +1,29 @@ [build-system] -requires = ["hatchling>=1.5"] +requires = ["hatchling~=1.21"] build-backend = "hatchling.build" [project] name = "jupyter_nbmodel_client" -authors = [{name = "Datalayer", email = "info@datalayer.io"}] +authors = [{ name = "Datalayer", email = "info@datalayer.io" }] dynamic = ["version"] readme = "README.md" -requires-python = ">=3.8" -keywords = ["Jupyter", "Extension"] +requires-python = ">=3.9" +keywords = ["Jupyter"] classifiers = [ - "License :: OSI Approved :: BSD License", - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Framework :: Jupyter", + "License :: OSI Approved :: BSD License", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Framework :: Jupyter", ] dependencies = ["jupyter_server>=1.6,<3"] [project.optional-dependencies] -test = [ - "pytest>=7.0", - "pytest-jupyter[server]>=0.6" -] -lint = [ - "black>=22.6.0", - "mdformat>0.7", - "mdformat-gfm>=0.3.5", - "ruff>=0.0.156" -] +test = ["pytest>=7.0", "pytest-jupyter[server]>=0.6"] +lint = ["mdformat>0.7", "mdformat-gfm>=0.3.5", "ruff"] typing = ["mypy>=0.990"] [project.license] -file="LICENSE" +file = "LICENSE" [project.urls] Home = "https://github.com/datalayer/jupyter-nbmodel-client" @@ -43,9 +31,6 @@ Home = "https://github.com/datalayer/jupyter-nbmodel-client" [tool.hatch.version] path = "jupyter_nbmodel_client/__init__.py" -[tool.hatch.build.targets.wheel.shared-data] -"jupyter-config" = "etc/jupyter" - [tool.pytest.ini_options] filterwarnings = [ "error", @@ -67,25 +52,35 @@ warn_unused_configs = true warn_unused_ignores = true warn_redundant_casts = true -[tool.black] -line-length = 100 -target-version = ["py38"] -skip-string-normalization = true - [tool.ruff] -target-version = "py38" +target-version = "py39" line-length = 100 + +[tool.ruff.lint] select = [ - "A", "B", "C", "E", "F", "FBT", "I", "N", "Q", "RUF", "S", "T", - "UP", "W", "YTT", + "A", + "B", + "C", + "E", + "F", + "FBT", + "I", + "N", + "Q", + "RUF", + "S", + "T", + "UP", + "W", + "YTT", ] ignore = [ -# Q000 Single quotes found but double quotes preferred -"Q000", -# FBT001 Boolean positional arg in function definition -"FBT001", "FBT002", "FBT003", -# C901 `foo` is too complex (12) -"C901", + # FBT001 Boolean positional arg in function definition + "FBT001", + "FBT002", + "FBT003", + # C901 `foo` is too complex (12) + "C901", ] [tool.ruff.per-file-ignores]