From ec8fe79da90603bcfcfc8b0fe07db1fcd18710e9 Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Sat, 7 Jan 2023 14:46:58 -0600 Subject: [PATCH] use hatch --- .github/workflows/linkcheck.yaml | 4 +- .github/workflows/publish.yaml | 28 +++- .github/workflows/test.yaml | 64 +++++--- CONTRIBUTING.md | 13 +- README.md | 33 ++-- RELEASE.md | 4 +- .../jupyter-server-proxy.json} | 0 .../jupyter-server-proxy.json} | 0 .../tree.d/jupyter-server-proxy.json} | 0 labextension/README.md | 9 +- labextension/package.json | 2 +- pyproject.toml | 114 +++++++++++++- setup.cfg | 2 - setup.py | 145 +----------------- 14 files changed, 207 insertions(+), 211 deletions(-) rename jupyter_server_proxy/etc/{jupyter-server-proxy-notebookserverextension.json => jupyter_notebook_config.d/jupyter-server-proxy.json} (100%) rename jupyter_server_proxy/etc/{jupyter-server-proxy-jupyterserverextension.json => jupyter_server_config.d/jupyter-server-proxy.json} (100%) rename jupyter_server_proxy/etc/{jupyter-server-proxy-nbextension.json => nbconfig/tree.d/jupyter-server-proxy.json} (100%) delete mode 100644 setup.cfg diff --git a/.github/workflows/linkcheck.yaml b/.github/workflows/linkcheck.yaml index 45596f64..71e1b871 100644 --- a/.github/workflows/linkcheck.yaml +++ b/.github/workflows/linkcheck.yaml @@ -25,9 +25,11 @@ jobs: - uses: actions/setup-python@v4 with: python-version: "3.11" + cache: pip + cache-dependency-path: pyproject.toml - name: Install deps - run: pip install -r docs/requirements.txt + run: pip install .[docs] - name: make linkcheck run: | diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 9d915c09..d7f9d062 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -35,14 +35,29 @@ jobs: steps: - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 with: python-version: "3.11" + cache: pip + cache-dependency-path: pyproject.toml + + - uses: actions/setup-node@v3 + with: + cache: yarn + node-version: 18.x + registry-url: https://registry.npmjs.org + cache-dependency-path: labextension/yarn.lock + + - name: Update root build packages + run: | + pip install --upgrade build pip jupyterlab~=3.0 - name: Build dist run: | - pip install jupyter_packaging wheel jupyterlab~=3.0 - python setup.py sdist bdist_wheel + set -eux + pyproject-build + cd dist && sha256sum * | tee SHA256SUMS - name: Check dist sizes run: | @@ -52,9 +67,12 @@ jobs: - name: Javascript package run: | + set -eux mkdir jsdist cd labextension jlpm pack --filename ../jsdist/labextension-jlpmpack.tgz + cd ../jsdist && sha256sum * | tee SHA256SUMS + - name: Upload Python artifact uses: actions/upload-artifact@v3 @@ -112,9 +130,11 @@ jobs: name: jsdist path: jsdist - - run: npm publish --dry-run ./jsdist/labextension-jlpmpack.tgz + - run: | + npm publish --dry-run ./jsdist/labextension-jlpmpack.tgz - - run: npm publish ./jsdist/labextension-jlpmpack.tgz + - run: | + npm publish ./jsdist/labextension-jlpmpack.tgz if: startsWith(github.ref, 'refs/tags') env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 5758124f..19eaf156 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -43,61 +43,79 @@ jobs: steps: - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 with: python-version: "${{ matrix.python-version }}" + cache: pip + cache-dependency-path: pyproject.toml + + - uses: actions/setup-node@v3 + with: + cache: yarn + node-version: 18.x + registry-url: https://registry.npmjs.org + cache-dependency-path: labextension/yarn.lock + + - name: Update root build packages + run: | + pip install --upgrade build pip - name: Build Python package run: | - pip install jupyter_packaging wheel jupyterlab - python setup.py sdist bdist_wheel + pyproject-build - - name: Install Python dependencies + - name: Install Python package # NOTE: See CONTRIBUTING.md for a local development setup that differs # slightly from this. # # Pytest options are set in tests/pytest.ini. run: | - pip install --upgrade pip - pip install jupyter_packaging jupyterlab~=${{ matrix.jupyterlab-version }}.0 - pip install ./dist/jupyter_server_proxy-*.whl - pip install pytest pytest-cov pytest-html - # Ensure we don't accidentally depend on notebook - if [ "${{ matrix.jupyter-app }}" == "notebook" ]; then - pip install "notebook<7" - fi + set -eux + pip install -vv $(ls ./dist/jupyter_server_proxy-*.whl)\[acceptance\] 'jupyterlab~=${{ matrix.jupyterlab-version }}.0' + + - name: List Python packages + run: | + set -eux pip freeze + pip check - name: Run tests run: | + set -eux JUPYTER_TOKEN=secret jupyter-${{ matrix.jupyter-app }} --config=./tests/resources/jupyter_server_config.py & sleep 5 cd tests - pytest + pytest -k "not acceptance" - name: Upload pytest and coverage reports if: always() uses: actions/upload-artifact@v3 with: - name: unit-tests-${{ matrix.python-version }}-${{ matrix.jupyter-app }}-${{ matrix.jupyterlab-version }}-${{ github.run_number }} + name: |- + unit-tests-${{ matrix.python-version }}-${{ matrix.jupyter-app }}-${{ matrix.jupyterlab-version }}-${{ github.run_number }} path: | ./build/pytest ./build/coverage - name: Check the Notebook Server extension is installed run: | + set -eux jupyter serverextension list - jupyter serverextension list 2>&1 | grep -ie "jupyter_server_proxy.*enabled" - + jupyter serverextension list 2>&1 | grep -iE "jupyter_server_proxy.*OK" - - name: Check the Jupyter Server extension is installed run: | + set -eux pip install jupyter-server jupyter server extension list - jupyter server extension list 2>&1 | grep -ie "jupyter_server_proxy.*enabled" - + jupyter server extension list 2>&1 | grep -iE "jupyter_server_proxy.*OK" - - name: Install JupyterLab Extension if: matrix.jupyterlab-version == '2' run: | + set -eux + export NODE_OPTIONS=--openssl-legacy-provider cd labextension jupyter labextension install . --no-build --debug jupyter lab build --minimize=False --debug @@ -108,25 +126,23 @@ jobs: # jupyterlab.browser_check with jupyterlab 2 and a modern version of # python (3.11+). # - if: ${{ !(matrix.jupyterlab-version == '2' && startsWith(matrix.python-version, '3.1')) }} + if: ${{ !(matrix.jupyterlab-version == '2' && startsWith(matrix.python-version, '3.11')) }} run: | + set -eux jupyter labextension list - jupyter labextension list 2>&1 | grep -ie '@jupyterhub/jupyter-server-proxy.*OK.*' + jupyter labextension list 2>&1 | grep -iE '@jupyterhub/jupyter-server-proxy.*OK.*' python -m jupyterlab.browser_check - - name: Install Acceptance test dependencies - run: | - # the acceptance test requires notebook to run - pip install "notebook<7" "robotframework-jupyterlibrary>=0.4.2" - - name: Run acceptance tests run: | - pytest -k acceptance -s + set -eux + pytest -s -k "acceptance" - name: Upload acceptance test reports if: always() uses: actions/upload-artifact@v3 with: - name: acceptance-tests-${{ matrix.python-version }}-${{ matrix.jupyter-app }}-${{ matrix.jupyterlab-version }}-${{ github.run_number }} + name: |- + acceptance-tests-${{ matrix.python-version }}-${{ matrix.jupyter-app }}-${{ matrix.jupyterlab-version }}-${{ github.run_number }} path: | ./build/robot diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8b019aaa..60fd6cc0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,7 +39,7 @@ To install these in addition to the [Python package](#python-package) test dependencies, run: ```bash -pip install -e .[test,acceptance] +pip install -e .[acceptance] ``` In addition, compatible versions of: @@ -47,7 +47,7 @@ In addition, compatible versions of: - `geckodriver` - `firefox` -Needs to be on your `$PATH` and compatible with each other. +Need to be on your `$PATH` and compatible with each other. To run _only_ the acceptance tests, use the `-k` switch: @@ -74,11 +74,10 @@ jlpm build:prod # Build: jlpm install:extension # Symlink into `{sys.prefix}/share/jupyter/labextensions` ``` -You can watch the source directory and automatically rebuild the `lib` folder: +You can watch the source directory and automatically rebuild the `labextension/lib` +and `jupyter_server_proxy/labextension` folders: ```bash -jlpm watch # ... watch the source directory in another terminal tab +cd labextension +jlpm watch ``` - -However, the built-in `jupyter labextension watch` does _not_ work with this repo, -as the `package.json` and `setup.py` would need to be at the same level. diff --git a/README.md b/README.md index b908e527..469fba4a 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ contains information on installation & usage. ## Security warning Jupyter Server Proxy is often used to start a user defined process listening to -some network port (e.g. http://localhost:4567) for a user starting a Jupyter Server +some network port (e.g. `http://localhost:4567`) for a user starting a Jupyter Server that only that user has permission to access. The user can then access the started process proxied through the Jupyter Server. @@ -47,7 +47,7 @@ A common strategy to enforce access proxied via Jupyter Server is to start Jupyter Server within a container and only allow network access to the Jupyter Server via the container. -For more insights, see [Ryan Lovetts comment about +> For more insights, see [Ryan Lovett's comment about it](https://github.com/jupyterhub/jupyter-server-proxy/pull/359#issuecomment-1350118197). ## Install @@ -58,18 +58,22 @@ it](https://github.com/jupyterhub/jupyter-server-proxy/pull/359#issuecomment-135 ### Python package -#### pip +#### `pip` -``` +```bash pip install jupyter-server-proxy ``` -#### conda +#### `conda` -``` +```bash conda install jupyter-server-proxy -c conda-forge ``` +#### Local development + +> See the [contributing guide](https://github.com/jupyterhub/jupyter-server-proxy/blob/main/CONTRIBUTING.md). + ### JupyterLab extension Note that as the JupyterLab extension only is a graphical interface to @@ -79,7 +83,7 @@ requires the python package to be installed. As of version 3.0.0 the Python package ships with a JupyterLab 3 compatible extension, making this step only needed for JupyterLab 2. -``` +```bash jupyter labextension install @jupyterhub/jupyter-server-proxy ``` @@ -87,22 +91,19 @@ jupyter labextension install @jupyterhub/jupyter-server-proxy ### Server extension -```  -jupyter serverextension disable jupyter_server_proxy +```bash +jupyter serverextension disable --sys-prefix jupyter_server_proxy +jupyter server extension disable --sys-prefix jupyter_server_proxy ``` ### Notebook classic extension -``` -jupyter nbextension disable --py jupyter_server_proxy +```bash +jupyter nbextension disable --sys-prefix --py jupyter_server_proxy ``` ### JupyterLab extension -``` +```bash jupyter labextension disable @jupyterhub/jupyter-server-proxy ``` - -## Local development - -See [CONTRIBUTING.md](CONTRIBUTING.md). diff --git a/RELEASE.md b/RELEASE.md index 144e0bb3..f7ce7baa 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -20,8 +20,8 @@ These are instructions on how to make a release. ```shell rm labextension/yarn.lock - pip install jupyter_packaging wheel jupyterlab~=3.0 - python setup.py sdist bdist_wheel + pip install --upgrade pip build + pyproject-build ``` 2. Create a PR updating `docs/source/changelog.md` with [github-activity][] and diff --git a/jupyter_server_proxy/etc/jupyter-server-proxy-notebookserverextension.json b/jupyter_server_proxy/etc/jupyter_notebook_config.d/jupyter-server-proxy.json similarity index 100% rename from jupyter_server_proxy/etc/jupyter-server-proxy-notebookserverextension.json rename to jupyter_server_proxy/etc/jupyter_notebook_config.d/jupyter-server-proxy.json diff --git a/jupyter_server_proxy/etc/jupyter-server-proxy-jupyterserverextension.json b/jupyter_server_proxy/etc/jupyter_server_config.d/jupyter-server-proxy.json similarity index 100% rename from jupyter_server_proxy/etc/jupyter-server-proxy-jupyterserverextension.json rename to jupyter_server_proxy/etc/jupyter_server_config.d/jupyter-server-proxy.json diff --git a/jupyter_server_proxy/etc/jupyter-server-proxy-nbextension.json b/jupyter_server_proxy/etc/nbconfig/tree.d/jupyter-server-proxy.json similarity index 100% rename from jupyter_server_proxy/etc/jupyter-server-proxy-nbextension.json rename to jupyter_server_proxy/etc/nbconfig/tree.d/jupyter-server-proxy.json diff --git a/labextension/README.md b/labextension/README.md index 025b6b6b..238bca96 100644 --- a/labextension/README.md +++ b/labextension/README.md @@ -1,8 +1,7 @@ -# @jupyterhub/jupyter-server-proxy labextension +# `@jupyterhub/jupyter-server-proxy` -This JupyterLab extension that is bundled and requires with the PyPI package -jupyter-server-proxy adds items to the JupyterLab [Launcher] representing the -configured server processes. +A JupyterLab extension that adds items to the JupyterLab [Launcher] representing the +configured server processes managed by the python package `jupyter-server-proxy` (required). [launcher]: https://jupyterlab.readthedocs.io/en/stable/extension/extension_points.html#launcher @@ -25,7 +24,7 @@ conda install jupyter-server-proxy ``` > As a _prebuilt_ extension, it will "just work," only a simple page reload should be required -> to see launcher items. However, a full restart of `jupyter_server` or `notebook` is required +> to see launcher items. However, a full restart of `jupyter_server` or `notebook` is required > to reload the `jupyter_server_proxy` serverextension which provides most of the functionality. ### JupyterLab 2 diff --git a/labextension/package.json b/labextension/package.json index 30718c46..905a1ff7 100644 --- a/labextension/package.json +++ b/labextension/package.json @@ -38,7 +38,7 @@ "clean:all": "jlpm run clean:lib && jlpm run clean:labextension", "install:extension": "jupyter labextension develop --overwrite .", "watch": "run-p watch:src watch:labextension", - "watch:src": "jlpm build:lib -w", + "watch:src": "jlpm build:lib -w --preserveWatchOutput", "watch:labextension": "jupyter labextension watch .", "deduplicate": "yarn-deduplicate -s fewer --fail" }, diff --git a/pyproject.toml b/pyproject.toml index 6394cd86..98b52f68 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,114 @@ -# build-system -# - ref 1: https://peps.python.org/pep-0517/ -# [build-system] -requires = ["jupyter_packaging~=0.7.9", "jupyterlab~=3.0", "setuptools>=40.8.0", "wheel"] -build-backend = "setuptools.build_meta" +build-backend = "hatchling.build" +requires = [ + "hatch-jupyter-builder >=0.5", + "hatch-nodejs-version", + "hatchling >=1.4.0", + "jupyterlab >=3.4.7,<4.0.0", +] + +[project] +name = "jupyter_server_proxy" +dynamic = [ + "authors", + "description", + "keywords", + "urls", + "version", +] +readme = "README.md" +license = { file = "LICENSE" } +requires-python = ">=3.7" +classifiers = [ + "Framework :: Jupyter", + "Framework :: Jupyter :: JupyterLab", + "Framework :: Jupyter :: JupyterLab :: 2", + "Framework :: Jupyter :: JupyterLab :: 3", + "Framework :: Jupyter :: JupyterLab :: Extensions :: Prebuilt", + "Framework :: Jupyter :: JupyterLab :: Extensions", + "License :: OSI Approved :: BSD License", + "Operating System :: MacOS :: MacOS X", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", +] +dependencies = [ + "aiohttp", + "jupyter-server >=1.0", + "simpervisor >=0.4", +] + +[project.optional-dependencies] +test = [ + "pytest", + "pytest-cov", + "pytest-html", +] +acceptance = [ + "jupyter-server-proxy[test]", + "notebook <7", + "robotframework-jupyterlibrary >=0.4.2", +] +docs = [ + "myst-parser", + "sphinx-autobuild", + "sphinx-book-theme", + "sphinx-copybutton", + "sphinxext-opengraph", + "sphinxext-rediraffe", +] + +[tool.hatch.version] +source = "nodejs" +path = "labextension/package.json" + +[tool.hatch.build.targets.sdist] +artifacts = [ + "jupyter_server_proxy/labextension", +] +exclude = [ + ".git", + ".github", + ".readthedocs.yaml", + "contrib", + "docs", +] + +[tool.hatch.build.targets.wheel.shared-data] +"jupyter_server_proxy/etc" = "etc/jupyter" +"jupyter_server_proxy/labextension" = "share/jupyter/labextensions/@jupyterhub/jupyter-server-proxy" +"jupyter_server_proxy/static" = "share/jupyter/nbextensions/jupyter_server_proxy" + +[tool.hatch.metadata.hooks.nodejs] +path = "labextension/package.json" +fields = ["description", "authors", "urls"] + +[tool.hatch.build.hooks.jupyter-builder] +build-function = "hatch_jupyter_builder.npm_builder" +ensured-targets = [ + "jupyter_server_proxy/labextension/static/style.js", + "jupyter_server_proxy/labextension/package.json", + "jupyter_server_proxy/labextension/static/third-party-licenses.json", +] +skip-if-exists = ["jupyter_server_proxy/labextension/package.json"] + +[tool.hatch.build.hooks.jupyter-builder.build-kwargs] +path = "labextension" +build_cmd = "build:prod" +npm = ["jlpm"] +[tool.hatch.build.hooks.jupyter-builder.editable-build-kwargs] +path = "labextension" +build_cmd = "install:extension" +npm = ["jlpm"] +source_dir = "labextension/src" +build_dir = "jupyter_server_proxy/labextension" # tbump is used to simplify and standardize the release process when updating # the version, making a git commit and tag, and pushing changes. diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 8183238a..00000000 --- a/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[metadata] -license_files = LICENSE diff --git a/setup.py b/setup.py index 38eda7ff..cc0b3a54 100644 --- a/setup.py +++ b/setup.py @@ -1,144 +1 @@ -""" -jupyter-server-proxy setup -""" -import json -from glob import glob -from pathlib import Path - -import setuptools -from jupyter_packaging import ( - combine_commands, - create_cmdclass, - ensure_targets, - install_npm, - skip_if_exists, -) - -HERE = Path(__file__).parent.resolve() - -# The name of the project -name = "jupyter_server_proxy" - -lab_path = HERE / name / "labextension" - -# Representative files that should exist after a successful build -jstargets = [ - str(lab_path / "package.json"), -] - -package_data_spec = { - name: ["*"], -} - -labext_name = "@jupyterhub/jupyter-server-proxy" - -data_files_spec = [ - ("share/jupyter/labextensions/%s" % labext_name, str(lab_path), "**"), - ("share/jupyter/labextensions/%s" % labext_name, str(HERE), "install.json"), - ( - "etc/jupyter/jupyter_server_config.d", - "jupyter_server_proxy/etc", - "jupyter-server-proxy-jupyterserverextension.json", - ), - ( - "etc/jupyter/jupyter_notebook_config.d", - "jupyter_server_proxy/etc", - "jupyter-server-proxy-notebookserverextension.json", - ), - ( - "etc/jupyter/nbconfig/tree.d", - "jupyter_server_proxy/etc", - "jupyter-server-proxy-nbextension.json", - ), -] - -cmdclass = create_cmdclass( - "jsdeps", package_data_spec=package_data_spec, data_files_spec=data_files_spec -) - -js_command = combine_commands( - install_npm(HERE / "labextension", build_cmd="build:prod", npm=["jlpm"]), - ensure_targets(jstargets), -) - -is_repo = (HERE / ".git").exists() -if is_repo: - cmdclass["jsdeps"] = js_command -else: - cmdclass["jsdeps"] = skip_if_exists(jstargets, js_command) - -long_description = (HERE / "README.md").read_text() - -# Get the package info from package.json -pkg_json = json.loads((HERE / "labextension" / "package.json").read_bytes()) - -setup_args = dict( - name=name.replace("_", "-"), - version=pkg_json["version"], - url=pkg_json["homepage"], - author=pkg_json["author"]["name"], - author_email=pkg_json["author"]["email"], - description=pkg_json["description"], - license=pkg_json["license"], - long_description=long_description, - long_description_content_type="text/markdown", - cmdclass=cmdclass, - packages=setuptools.find_packages(), - install_requires=[ - "aiohttp", - "jupyter-server>=1.0", - "simpervisor>=0.4", - ], - extras_require={ - # acceptance tests additionally require firefox and geckodriver - "test": [ - "pytest", - "pytest-cov", - "pytest-html" - ], - "acceptance": [ - "robotframework-jupyterlibrary>=0.4.2" - ] - }, - zip_safe=False, - include_package_data=True, - python_requires=">=3.7", - keywords=["Jupyter", "JupyterLab", "JupyterLab3"], - classifiers=[ - "Framework :: Jupyter", - "Framework :: Jupyter :: JupyterLab :: 2", - "Framework :: Jupyter :: JupyterLab :: 3", - "Framework :: Jupyter :: JupyterLab :: Extensions", - "Framework :: Jupyter :: JupyterLab :: Extensions :: Prebuilt", - "Operating System :: Microsoft :: Windows", - "Operating System :: POSIX :: Linux", - "Operating System :: MacOS :: MacOS X", - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Framework :: Jupyter", - ], - data_files=[ - ("share/jupyter/nbextensions/jupyter_server_proxy", glob("jupyter_server_proxy/static/*")), - ( - "etc/jupyter/jupyter_notebook_config.d", - ["jupyter_server_proxy/etc/jupyter-server-proxy-notebookserverextension.json"], - ), - ( - "etc/jupyter/jupyter_server_config.d", - ["jupyter_server_proxy/etc/jupyter-server-proxy-jupyterserverextension.json"], - ), - ( - "etc/jupyter/nbconfig/tree.d", - ["jupyter_server_proxy/etc/jupyter-server-proxy-nbextension.json"], - ), - ], -) - - -if __name__ == "__main__": - setuptools.setup(**setup_args) +# this file intentionally left blank for legacy tools to find