From 43384a3d3f6cd107586939ae134d80d8681cfa49 Mon Sep 17 00:00:00 2001 From: Jeremy Tuloup Date: Wed, 14 Apr 2021 13:19:19 +0200 Subject: [PATCH] Use Jupyter Packaging --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/python-linux.yml | 24 +- .github/workflows/python-macos.yml | 2 +- .github/workflows/python-windows.yml | 2 +- CHANGELOG.md | 14 - CONTRIBUTING.rst | 2 +- MANIFEST.in | 1 + README.md | 8 +- RELEASE.md | 27 +- jupyter_server/_version.py | 14 +- package-lock.json | 496 ++++-------------- pyproject.toml | 27 + pytest.ini | 3 - setup.cfg | 62 ++- setup.py | 84 +-- setupbase.py | 720 -------------------------- 16 files changed, 247 insertions(+), 1241 deletions(-) create mode 100644 pyproject.toml delete mode 100644 pytest.ini delete mode 100644 setupbase.py diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 07f62968e2..e90f4b25ce 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -14,7 +14,7 @@ name: "CodeQL" on: push: - branches: [ '*' ] + branches: [ 'master' ] pull_request: # The branches below must be a subset of the branches above branches: [ master ] diff --git a/.github/workflows/python-linux.yml b/.github/workflows/python-linux.yml index ffa45ccf9e..8fedfc3ecc 100644 --- a/.github/workflows/python-linux.yml +++ b/.github/workflows/python-linux.yml @@ -1,7 +1,7 @@ name: Jupyter Server Tests [Linux] on: push: - branches: '*' + branches: 'master' pull_request: branches: '*' jobs: @@ -37,7 +37,7 @@ jobs: ${{ runner.os }}-pip- - name: Install the Python dependencies run: | - pip install -e .[test] codecov + pip install -e ".[test]" codecov - name: List installed packages run: | pip freeze @@ -62,7 +62,19 @@ jobs: codecov - name: Test full install run: | - pip uninstall -y jupyter_server - pip install . - cd /tmp - pytest --pyargs jupyter_server + python -m venv test_install + ./test_install/bin/python -m pip install -U pip + ./test_install/bin/python -m pip install ".[test]" + pushd test_install + ./bin/pytest --pyargs jupyter_server + popd + - name: Check the Manifest + run: | + pip install check-manifest + git clean -dfx + check-manifest -v + - name: Check Version Bump + run: | + pip install tbump + tbump --non-interactive --only-patch 100.1.1 + git checkout . diff --git a/.github/workflows/python-macos.yml b/.github/workflows/python-macos.yml index 94a7800fdc..163e74a525 100644 --- a/.github/workflows/python-macos.yml +++ b/.github/workflows/python-macos.yml @@ -1,7 +1,7 @@ name: Jupyter Server Tests [Mac OS] on: push: - branches: '*' + branches: 'master' pull_request: branches: '*' jobs: diff --git a/.github/workflows/python-windows.yml b/.github/workflows/python-windows.yml index 347f487fc0..525aa9d7ed 100644 --- a/.github/workflows/python-windows.yml +++ b/.github/workflows/python-windows.yml @@ -1,7 +1,7 @@ name: Jupyter Server Tests [Windows] on: push: - branches: '*' + branches: 'master' pull_request: branches: '*' jobs: diff --git a/CHANGELOG.md b/CHANGELOG.md index dab43ebd05..974bf90de2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,6 @@ All notable changes to this project will be documented in this file. ## 1.6.1 - ([Full Changelog](https://github.com/jupyter-server/jupyter_server/compare/1.6.0...2756a29c5fdcfa62a3492004627541089d53d14f)) ### Merged PRs @@ -94,7 +93,6 @@ All notable changes to this project will be documented in this file. [@afshin](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Aafshin+updated%3A2021-02-22..2021-03-23&type=Issues) | [@blink1073](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Ablink1073+updated%3A2021-02-22..2021-03-23&type=Issues) | [@codecov-io](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Acodecov-io+updated%3A2021-02-22..2021-03-23&type=Issues) | [@jtpio](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Ajtpio+updated%3A2021-02-22..2021-03-23&type=Issues) | [@kevin-bates](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Akevin-bates+updated%3A2021-02-22..2021-03-23&type=Issues) | [@kiendang](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Akiendang+updated%3A2021-02-22..2021-03-23&type=Issues) | [@minrk](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Aminrk+updated%3A2021-02-22..2021-03-23&type=Issues) | [@sngyo](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Asngyo+updated%3A2021-02-22..2021-03-23&type=Issues) | [@Zsailer](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3AZsailer+updated%3A2021-02-22..2021-03-23&type=Issues) - ## [1.4.1](https://github.com/jupyter-server/jupyter_server/tree/1.4.1) (2021-02-22) [Full Changelog](https://github.com/jupyter-server/jupyter_server/compare/1.4.0...bc252d33de2f647f98d048dc32888f0a83f005ac) @@ -112,10 +110,8 @@ All notable changes to this project will be documented in this file. [@jamesmishra](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Ajamesmishra+updated%3A2021-02-18..2021-02-22&type=Issues) | [@Zsailer](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3AZsailer+updated%3A2021-02-18..2021-02-22&type=Issues) - ## [1.4.0](https://github.com/jupyter-server/jupyter_server/tree/1.4.0) (2021-02-18) - [Full Changelog](https://github.com/jupyter-server/jupyter_server/compare/1.3.0...HEAD) **Merged pull requests:** @@ -130,7 +126,6 @@ All notable changes to this project will be documented in this file. - \[Gateway\] Track only this server's kernels [\#407](https://github.com/jupyter-server/jupyter_server/pull/407) ([kevin-bates](https://github.com/kevin-bates)) - Update manager.py: more descriptive warnings when extensions fail to load [\#396](https://github.com/jupyter-server/jupyter_server/pull/396) ([alberti42](https://github.com/alberti42)) - ## [1.3.0](https://github.com/jupyter-server/jupyter_server/tree/1.3.0) (2021-02-04) [Full Changelog](https://github.com/jupyter-server/jupyter_server/compare/1.2.2...HEAD) @@ -161,7 +156,6 @@ This was a broken release and was yanked from PyPI. - Increase culling test idle timeout [\#388](https://github.com/jupyter-server/jupyter_server/pull/388) ([kevin-bates](https://github.com/kevin-bates)) - update changelog for 1.2.2 [\#387](https://github.com/jupyter-server/jupyter_server/pull/387) ([Zsailer](https://github.com/Zsailer)) - ## [1.2.2](https://github.com/jupyter-server/jupyter_server/tree/1.2.2) (2021-01-14) **Merged pull requests:** @@ -172,7 +166,6 @@ This was a broken release and was yanked from PyPI. - Replace secure\_write, is\_hidden, exists with jupyter\_core's [\#382](https://github.com/jupyter-server/jupyter_server/pull/382) ([kevin-bates](https://github.com/kevin-bates)) - Add --autoreload flag [\#380](https://github.com/jupyter-server/jupyter_server/pull/380) ([afshin](https://github.com/afshin)) - ## [1.2.1](https://github.com/jupyter-server/jupyter_server/tree/1.2.1) (2021-01-08) [Full Changelog](https://github.com/jupyter-server/jupyter_server/compare/1.2.0...1.2.1) @@ -221,7 +214,6 @@ This was a broken release and was yanked from PyPI. - Nudge kernel with info request until we receive IOPub messages [\#361](https://github.com/jupyter-server/jupyter_server/pull/361) ([SylvainCorlay](https://github.com/SylvainCorlay)) - ## [1.1.1](https://github.com/jupyter-server/jupyter_server/tree/1.1.1) (2020-12-16) [Full Changelog](https://github.com/jupyter-server/jupyter_server/compare/1.1.0...1.1.1) @@ -230,7 +222,6 @@ This was a broken release and was yanked from PyPI. - Fix: await possible async dir\_exists method [\#363](https://github.com/jupyter-server/jupyter_server/pull/363) ([mwakaba2](https://github.com/mwakaba2)) - ## 1.1.0 (2020-12-11) [Full Changelog](https://github.com/jupyter-server/jupyter_server/compare/1.0.10...1.1.0) @@ -252,7 +243,6 @@ This was a broken release and was yanked from PyPI. - Enable CodeQL runs on all pushed branches [\#333](https://github.com/jupyter-server/jupyter_server/pull/333) ([kevin-bates](https://github.com/kevin-bates)) - Asynchronous Contents API [\#324](https://github.com/jupyter-server/jupyter_server/pull/324) ([mwakaba2](https://github.com/mwakaba2)) - ## 1.0.6 (2020-11-18) 1.0.6 is a security release, fixing one vulnerability: @@ -261,7 +251,6 @@ This was a broken release and was yanked from PyPI. - Fix open redirect vulnerability GHSA-grfj-wjv9-4f9v (CVE-2020-26232) - ## 1.0 (2020-9-18) ### Added. @@ -293,7 +282,6 @@ This was a broken release and was yanked from PyPI. * Changed ExtensionApp mixin to inherit from `HasTraits`. This broke in traitlets 5.0 ([294](https://github.com/jupyter/jupyter_server/pull/294)) * Replaces `urlparse` with `url_path_join` to prevent URL squashing issues. ([304](https://github.com/jupyter/jupyter_server/pull/304)) - ## [0.3] - 2020-4-22 ### Added @@ -301,7 +289,6 @@ This was a broken release and was yanked from PyPI. - ([#191](https://github.com/jupyter/jupyter_server/pull/191)) Async kernel managment is now possible using the `AsyncKernelManager` from `jupyter_client` - ([#201](https://github.com/jupyter/jupyter_server/pull/201)) Parameters can now be passed to new terminals created by the `terminals` REST API. - ### Changed - ([#196](https://github.com/jupyter/jupyter_server/pull/196)) Documentation was rewritten + refactored to use pydata_sphinx_theme. @@ -311,7 +298,6 @@ This was a broken release and was yanked from PyPI. - ([#194](https://github.com/jupyter/jupyter_server/pull/194)) The bundlerextension entry point was removed. - ## [0.2.1] - 2020-1-10 ### Added diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 7f078f5653..6bdbc28330 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -99,4 +99,4 @@ 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/contrib_docs/index.html +__ https://jupyter.readthedocs.io/en/latest/contributing/content-contributor.html diff --git a/MANIFEST.in b/MANIFEST.in index e8b3aad87e..84c24303cc 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -4,6 +4,7 @@ include README.md include RELEASE.md include CHANGELOG.md include setupbase.py +include package.json # include everything in package_data recursive-include jupyter_server * diff --git a/README.md b/README.md index 304f518a58..91de88fae8 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ For more information, read our [documentation here](http://jupyter-server.readth 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 + pip install jupyter_server Jupyter Server currently supports Python>=3.6 on Linux, OSX and Windows. @@ -31,14 +31,14 @@ To see the changes between releases, checkout the [CHANGELOG](https://github.com Launch with: - $ jupyter server + jupyter server ### Testing To test an installed `jupyter_server`, run the following: - $ pip install jupyter_server[test] - $ pytest jupyter_server + pip install jupyter_server[test] + pytest jupyter_server ## Contributing diff --git a/RELEASE.md b/RELEASE.md index 51bed6c020..250bd77d1c 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,41 +1,44 @@ # Making a Jupyter Server Release To create a release, perform the following steps... -## Remove untracked files +## Set up ``` +pip install tbump twine build +git pull origin $(git branch --show-current) git clean -dffx ``` ## Update the version and apply the tag ``` -vim jupyter_server/_version.py -export script_version=`python setup.py --version 2>/dev/null` -git commit -a -m "Release $script_version" -git tag $script_version +echo "Enter new version" +read script_version +tbump ${script_version} +git commit -a -m "Release ${script_version}" +git tag $script_version -m "Release ${script_version}" ``` ## Build the artifacts ``` rm -rf dist -python setup.py sdist -python setup.py bdist_wheel +python -m build . ``` ## Update the version back to dev ``` -vim jupyter_server/_version.py +echo "Enter dev version" +read dev_version +tbump ${dev_version} git commit -a -m "Back to dev version" ``` ## Push the commits and tag ``` -git push --all -git push --tags +git push origin $(git branch --show-current) +git push origin --tags ``` ## Publish the artifacts to pypi ``` -pip install twine -twine check dist/* +twine check dist/* twine upload dist/* ``` diff --git a/jupyter_server/_version.py b/jupyter_server/_version.py index a918001737..d260b1806a 100644 --- a/jupyter_server/_version.py +++ b/jupyter_server/_version.py @@ -2,14 +2,8 @@ store the current version info of the server. """ +from jupyter_packaging import get_version_info -# Downstream maintainer, when running `python.setup.py jsversion`, -# the version string is propagated to the JavaScript files, do not forget to -# patch the JavaScript files in `.postN` release done by distributions. - -# Next beta/alpha/rc release: The version number for beta is X.Y.ZbN **without dots**. -# However, be sure to INCLUDE a dot prefix when adding the dev release: X.Y.Z.devN -# See: https://www.python.org/dev/peps/pep-0440/#public-version-identifiers - -version_info = (1, 7, 0, '.dev0') -__version__ = '.'.join(map(str, version_info[:3])) + ''.join(version_info[3:]) +# Version string must appear intact for tbump versioning +__version__ = '1.7.0.dev0' +version_info = get_version_info(__version__) diff --git a/package-lock.json b/package-lock.json index bb520c6eb6..7186062416 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,374 +1,26 @@ { "name": "jupyter_server", "version": "1.0.0", - "lockfileVersion": 2, + "lockfileVersion": 1, "requires": true, - "packages": { - "": { - "version": "1.0.0", - "license": "BSD", - "dependencies": { - "bootstrap": "^3.4.0", - "copyfiles": "^2.4.1" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.0", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/balanced-match": { - "version": "1.0.0", - "license": "MIT" - }, - "node_modules/bootstrap": { - "version": "3.4.1", - "resolved": "http://localhost:5559/bootstrap/-/bootstrap-3.4.1.tgz", - "integrity": "sha512-yN5oZVmRCwe5aKwzRj6736nSmKDX7pLYwsXiCj/EYmo16hODaBiT4En5btW/jhBF/seV+XMx3aYwukYC3A49DA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "license": "MIT" - }, - "node_modules/copyfiles": { - "version": "2.4.1", - "license": "MIT", - "dependencies": { - "glob": "^7.0.5", - "minimatch": "^3.0.3", - "mkdirp": "^1.0.4", - "noms": "0.0.0", - "through2": "^2.0.1", - "untildify": "^4.0.0", - "yargs": "^16.1.0" - }, - "bin": { - "copyfiles": "copyfiles", - "copyup": "copyfiles" - } - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "license": "MIT" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "license": "MIT" - }, - "node_modules/escalade": { - "version": "3.1.1", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "license": "ISC" - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/glob": { - "version": "7.1.6", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "license": "ISC" - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/isarray": { - "version": "0.0.1", - "license": "MIT" - }, - "node_modules/minimatch": { - "version": "3.0.4", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/noms": { - "version": "0.0.0", - "license": "ISC", - "dependencies": { - "inherits": "^2.0.1", - "readable-stream": "~1.0.31" - } - }, - "node_modules/once": { - "version": "1.4.0", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "license": "MIT" - }, - "node_modules/readable-stream": { - "version": "1.0.34", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "license": "MIT" - }, - "node_modules/string_decoder": { - "version": "0.10.31", - "license": "MIT" - }, - "node_modules/string-width": { - "version": "4.2.2", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.0", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/through2": { - "version": "2.0.5", - "license": "MIT", - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/through2/node_modules/isarray": { - "version": "1.0.0", - "license": "MIT" - }, - "node_modules/through2/node_modules/readable-stream": { - "version": "2.3.7", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/through2/node_modules/string_decoder": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/untildify": { - "version": "4.0.0", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "license": "MIT" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "license": "ISC" - }, - "node_modules/xtend": { - "version": "4.0.2", - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "5.0.5", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "16.2.0", - "license": "MIT", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.7", - "license": "ISC", - "engines": { - "node": ">=10" - } - } - }, "dependencies": { "ansi-regex": { - "version": "5.0.0" + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" }, "ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "requires": { "color-convert": "^2.0.1" } }, "balanced-match": { - "version": "1.0.0" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "bootstrap": { "version": "3.4.1", @@ -377,6 +29,8 @@ }, "brace-expansion": { "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -384,6 +38,8 @@ }, "cliui": { "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "requires": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -392,18 +48,26 @@ }, "color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "requires": { "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.4" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "concat-map": { - "version": "0.0.1" + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "copyfiles": { "version": "2.4.1", + "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.4.1.tgz", + "integrity": "sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==", "requires": { "glob": "^7.0.5", "minimatch": "^3.0.3", @@ -415,22 +79,34 @@ } }, "core-util-is": { - "version": "1.0.2" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "emoji-regex": { - "version": "8.0.0" + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "escalade": { - "version": "3.1.1" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, "fs.realpath": { - "version": "1.0.0" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "get-caller-file": { - "version": "2.0.5" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "glob": { "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -442,31 +118,45 @@ }, "inflight": { "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "requires": { "once": "^1.3.0", "wrappy": "1" } }, "inherits": { - "version": "2.0.4" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "is-fullwidth-code-point": { - "version": "3.0.0" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, "isarray": { - "version": "0.0.1" + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" }, "minimatch": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "requires": { "brace-expansion": "^1.1.7" } }, "mkdirp": { - "version": "1.0.4" + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" }, "noms": { "version": "0.0.0", + "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz", + "integrity": "sha1-2o69nzr51nYJGbJ9nNyAkqczKFk=", "requires": { "inherits": "^2.0.1", "readable-stream": "~1.0.31" @@ -474,18 +164,26 @@ }, "once": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "requires": { "wrappy": "1" } }, "path-is-absolute": { - "version": "1.0.1" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "process-nextick-args": { - "version": "2.0.1" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "readable-stream": { "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -494,40 +192,56 @@ } }, "require-directory": { - "version": "2.1.1" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" }, "safe-buffer": { - "version": "5.1.2" - }, - "string_decoder": { - "version": "0.10.31" + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "string-width": { "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.0" } }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, "strip-ansi": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "requires": { "ansi-regex": "^5.0.0" } }, "through2": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "requires": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" }, "dependencies": { "isarray": { - "version": "1.0.0" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "readable-stream": { "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -540,6 +254,8 @@ }, "string_decoder": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" } @@ -547,13 +263,19 @@ } }, "untildify": { - "version": "4.0.0" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==" }, "util-deprecate": { - "version": "1.0.2" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "wrap-ansi": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -561,16 +283,24 @@ } }, "wrappy": { - "version": "1.0.2" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "xtend": { - "version": "4.0.2" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "y18n": { - "version": "5.0.5" + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz", + "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==" }, "yargs": { "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "requires": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -582,7 +312,9 @@ } }, "yargs-parser": { - "version": "20.2.7" + "version": "20.2.7", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", + "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==" } } } diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000..aad18428be --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,27 @@ +[build-system] +requires = ["jupyter_packaging~=0.9"] +build-backend = "jupyter_packaging.build_api" + +[tool.jupyter-packaging.builder] +factory = "jupyter_packaging.npm_builder" + +[tool.check-manifest] +ignore = ["tbump.toml", ".*", "*.yml", "package-lock.json", "bootstrap*"] + +[tool.pytest.ini_options] +# Exclude the example tests. +norecursedirs = "examples/*" + +[tool.tbump.version] +current = "1.7.0.dev0" +regex = ''' + (?P\d+)\.(?P\d+)\.(?P\d+) + ((?Pa|b|rc|.dev)(?P\d+))? +''' + +[tool.tbump.git] +message_template = "Bump to {new_version}" +tag_template = "v{new_version}" + +[[tool.tbump.file]] +src = "jupyter_server/_version.py" diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index 8e564d8748..0000000000 --- a/pytest.ini +++ /dev/null @@ -1,3 +0,0 @@ -[pytest] -# Exclude the example tests. -norecursedirs = examples/* diff --git a/setup.cfg b/setup.cfg index 0e107c1334..7c50708783 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,13 +1,55 @@ -[bdist_wheel] - [metadata] +name = jupyter_server +version = attr: jupyter_server.__version__ +description = The backend—i.e. core services, APIs, and REST endpoints—to Jupyter web applications. +long_description = file: README.md +long_description_content_type = text/markdown license_file = COPYING.md +author = Jupyter Development Team +author_email = jupyter@googlegroups.com +url = https://jupyter.org +platforms = Linux, Mac OS X, Windows +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.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + +[options] +zip_safe = False +include_package_data = True +packages = find: +python_requires = >=3.6 +install_requires = + jinja2 + tornado>=6.1.0 + pyzmq>=17 + argon2-cffi + ipython_genutils + traitlets>=4.2.1 + jupyter_core>=4.4.0 + jupyter_client>=6.1.1 + jupyter_packaging~=0.9 + nbformat + nbconvert + Send2Trash + terminado>=0.8.3 + prometheus_client + pywin32>=1.0 ; sys_platform == 'win32' + anyio>=2.0.2 + +[options.extras_require] +test = coverage; pytest; pytest-cov; pytest-mock; requests; pytest-tornasync; pytest-console-scripts; ipykernel + +[options.entry_points] +console_scripts = + jupyter-server = jupyter_server.serverapp:main -[nosetests] -warningfilters=module |.* |DeprecationWarning |jupyter_server.* - default |.* | Warning | jupyter_server.* - ignore |.*metadata.* |DeprecationWarning |jupyter_server.* - ignore |.*schema.* |UserWarning |nbfor.* - ignore |The 'warn' method is deprecated, use 'warning' instead | DeprecationWarning | jupyter_server.* - error |encodestring\(\) is a deprecated alias, use encodebytes\(\)| DeprecationWarning | jupyter_server.* - error |decodestring\(\) is a .*| DeprecationWarning | jupyter_server.* +[options.packages.find] +exclude = ['docs*', 'examples*'] diff --git a/setup.py b/setup.py index 464b9c2b85..e9e09be329 100644 --- a/setup.py +++ b/setup.py @@ -1,78 +1,10 @@ -import pathlib -from setuptools import ( - find_packages, - setup, -) -from setupbase import ( - get_version, create_cmdclass, install_npm -) +from setuptools import setup -here = pathlib.Path('.') -version_path = here.joinpath('jupyter_server', '_version.py') -VERSION = get_version(str(version_path)) +try: + from jupyter_packaging import wrap_installers, npm_builder + ensured_targets = ['jupyter_server/static/style/bootstrap.min.css'] + cmdclass = wrap_installers(pre_develop=npm_builder(), ensured_targets=ensured_targets) +except ImportError: + cmdclass = {} -readme_path = here.joinpath('README.md') -README = readme_path.read_text() - -cmdclass = create_cmdclass('jsdeps') -cmdclass['jsdeps'] = install_npm() - - -setup_args = dict( - name = 'jupyter_server', - description = 'The backend—i.e. core services, APIs, and REST endpoints—to Jupyter web applications.', - long_description = README, - long_description_content_type='text/markdown', - version = VERSION, - packages = find_packages('.', exclude=['tests*', 'docs*', 'examples*']), - include_package_data = True, - cmdclass = cmdclass, - author = 'Jupyter Development Team', - author_email = 'jupyter@googlegroups.com', - url = 'http://jupyter.org', - license = 'BSD', - platforms = "Linux, Mac OS X, Windows", - 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.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - ], - install_requires = [ - 'jinja2', - 'tornado>=6.1.0', - 'pyzmq>=17', - 'argon2-cffi', - 'ipython_genutils', - 'traitlets>=4.2.1', - 'jupyter_core>=4.4.0', - 'jupyter_client>=6.1.1', - 'nbformat', - 'nbconvert', - 'Send2Trash', - 'terminado>=0.8.3', - 'prometheus_client', - "pywin32>=1.0 ; sys_platform == 'win32'", - "anyio>=2.0.2", - ], - extras_require = { - 'test': ['coverage', 'requests', - 'pytest', 'pytest-cov', - 'pytest-tornasync', - 'pytest-console-scripts', 'ipykernel'], - }, - python_requires = '>=3.6', - entry_points = { - 'console_scripts': [ - 'jupyter-server = jupyter_server.serverapp:main', - ] - }, -) - -if __name__ == '__main__': - setup(**setup_args) +setup(cmdclass=cmdclass) diff --git a/setupbase.py b/setupbase.py deleted file mode 100644 index 45eb06c438..0000000000 --- a/setupbase.py +++ /dev/null @@ -1,720 +0,0 @@ -#!/usr/bin/env python -# coding: utf-8 - -# Copyright (c) Jupyter Development Team. -# Distributed under the terms of the Modified BSD License. - -""" -This file originates from the 'jupyter-packaging' package, and -contains a set of useful utilities for including npm packages -within a Python package. -""" -from collections import defaultdict -from os.path import join as pjoin -import io -import os -import functools -import pipes -import re -import shlex -import subprocess -import sys - - -# BEFORE importing distutils, remove MANIFEST. distutils doesn't properly -# update it when the contents of directories change. -if os.path.exists('MANIFEST'): os.remove('MANIFEST') - - -from setuptools import Command -from setuptools.command.build_py import build_py -from setuptools.command.sdist import sdist -from distutils import log - -from setuptools.command.develop import develop -from setuptools.command.bdist_egg import bdist_egg - -try: - from wheel.bdist_wheel import bdist_wheel -except ImportError: - bdist_wheel = None - -if sys.platform == 'win32': - from subprocess import list2cmdline -else: - def list2cmdline(cmd_list): - return ' '.join(map(pipes.quote, cmd_list)) - - -__version__ = '0.5.0' - -# --------------------------------------------------------------------------- -# Top Level Variables -# --------------------------------------------------------------------------- - -SEPARATORS = os.sep if os.altsep is None else os.sep + os.altsep - -if "--skip-npm" in sys.argv: - print("Skipping npm install as requested.") - skip_npm = True - sys.argv.remove("--skip-npm") -else: - skip_npm = False - - -# --------------------------------------------------------------------------- -# Public Functions -# --------------------------------------------------------------------------- - -def get_version(file, name='__version__'): - """Get the version of the package from the given file by - executing it and extracting the given `name`. - """ - path = os.path.realpath(file) - version_ns = {} - with io.open(path, encoding="utf8") as f: - exec(f.read(), {}, version_ns) - return version_ns[name] - - -def ensure_python(specs): - """Given a list of range specifiers for python, ensure compatibility. - """ - if not isinstance(specs, (list, tuple)): - specs = [specs] - v = sys.version_info - part = '%s.%s' % (v.major, v.minor) - for spec in specs: - if part == spec: - return - try: - if eval(part + spec): - return - except SyntaxError: - pass - raise ValueError('Python version %s unsupported' % part) - - -def update_package_data(distribution): - """update build_py options to get package_data changes""" - build_py = distribution.get_command_obj('build_py') - build_py.finalize_options() - - -class bdist_egg_disabled(bdist_egg): - """Disabled version of bdist_egg - - Prevents setup.py install performing setuptools' default easy_install, - which it should never ever do. - """ - def run(self): - sys.exit("Aborting implicit building of eggs. Use `pip install .` " - " to install from source.") - - -def create_cmdclass(prerelease_cmd=None, package_data_spec=None, - data_files_spec=None): - """Create a command class with the given optional prerelease class. - - Parameters - ---------- - prerelease_cmd: (name, Command) tuple, optional - The command to run before releasing. - package_data_spec: dict, optional - A dictionary whose keys are the dotted package names and - whose values are a list of glob patterns. - data_files_spec: list, optional - A list of (path, dname, pattern) tuples where the path is the - `data_files` install path, dname is the source directory, and the - pattern is a glob pattern. - - Notes - ----- - We use specs so that we can find the files *after* the build - command has run. - - The package data glob patterns should be relative paths from the package - folder containing the __init__.py file, which is given as the package - name. - e.g. `dict(foo=['./bar/*', './baz/**'])` - - The data files directories should be absolute paths or relative paths - from the root directory of the repository. Data files are specified - differently from `package_data` because we need a separate path entry - for each nested folder in `data_files`, and this makes it easier to - parse. - e.g. `('share/foo/bar', 'pkgname/bizz, '*')` - """ - wrapped = [prerelease_cmd] if prerelease_cmd else [] - if package_data_spec or data_files_spec: - wrapped.append('handle_files') - - wrapper = functools.partial(_wrap_command, wrapped) - handle_files = _get_file_handler(package_data_spec, data_files_spec) - develop_handler = _get_develop_handler() - - if 'bdist_egg' in sys.argv: - egg = wrapper(bdist_egg, strict=True) - else: - egg = bdist_egg_disabled - - is_repo = os.path.exists('.git') - - cmdclass = dict( - build_py=wrapper(build_py, strict=is_repo), - bdist_egg=egg, - sdist=wrapper(sdist, strict=True), - handle_files=handle_files, - ) - - if bdist_wheel: - cmdclass['bdist_wheel'] = wrapper(bdist_wheel, strict=True) - - cmdclass['develop'] = wrapper(develop_handler, strict=True) - return cmdclass - - -def command_for_func(func): - """Create a command that calls the given function.""" - - class FuncCommand(BaseCommand): - - def run(self): - func() - update_package_data(self.distribution) - - return FuncCommand - - -def run(cmd, **kwargs): - """Echo a command before running it.""" - log.info('> ' + list2cmdline(cmd)) - kwargs.setdefault('shell', os.name == 'nt') - if not isinstance(cmd, (list, tuple)) and os.name != 'nt': - cmd = shlex.split(cmd) - cmd_path = which(cmd[0]) - if not cmd_path: - sys.exit("Aborting. Could not find cmd (%s) in path. " - "If command is not expected to be in user's path, " - "use an absolute path." % cmd[0]) - cmd[0] = cmd_path - return subprocess.check_call(cmd, **kwargs) - - -def is_stale(target, source): - """Test whether the target file/directory is stale based on the source - file/directory. - """ - if not os.path.exists(target): - return True - target_mtime = recursive_mtime(target) or 0 - return compare_recursive_mtime(source, cutoff=target_mtime) - - -class BaseCommand(Command): - """Empty command because Command needs subclasses to override too much""" - user_options = [] - - def initialize_options(self): - pass - - def finalize_options(self): - pass - - def get_inputs(self): - return [] - - def get_outputs(self): - return [] - - -def combine_commands(*commands): - """Return a Command that combines several commands.""" - - class CombinedCommand(Command): - user_options = [] - - def initialize_options(self): - self.commands = [] - for C in commands: - self.commands.append(C(self.distribution)) - for c in self.commands: - c.initialize_options() - - def finalize_options(self): - for c in self.commands: - c.finalize_options() - - def run(self): - for c in self.commands: - c.run() - return CombinedCommand - - -def compare_recursive_mtime(path, cutoff, newest=True): - """Compare the newest/oldest mtime for all files in a directory. - - Cutoff should be another mtime to be compared against. If an mtime that is - newer/older than the cutoff is found it will return True. - E.g. if newest=True, and a file in path is newer than the cutoff, it will - return True. - """ - if os.path.isfile(path): - mt = mtime(path) - if newest: - if mt > cutoff: - return True - elif mt < cutoff: - return True - for dirname, _, filenames in os.walk(path, topdown=False): - for filename in filenames: - mt = mtime(pjoin(dirname, filename)) - if newest: # Put outside of loop? - if mt > cutoff: - return True - elif mt < cutoff: - return True - return False - - -def recursive_mtime(path, newest=True): - """Gets the newest/oldest mtime for all files in a directory.""" - if os.path.isfile(path): - return mtime(path) - current_extreme = None - for dirname, dirnames, filenames in os.walk(path, topdown=False): - for filename in filenames: - mt = mtime(pjoin(dirname, filename)) - if newest: # Put outside of loop? - if mt >= (current_extreme or mt): - current_extreme = mt - elif mt <= (current_extreme or mt): - current_extreme = mt - return current_extreme - - -def mtime(path): - """shorthand for mtime""" - return os.stat(path).st_mtime - - -def install_npm(path=None, build_dir=None, source_dir=None, build_cmd='build', - force=False, npm=None): - """Return a Command for managing an npm installation. - - Note: The command is skipped if the `--skip-npm` flag is used. - - Parameters - ---------- - path: str, optional - The base path of the node package. Defaults to the current directory. - build_dir: str, optional - The target build directory. If this and source_dir are given, - the JavaScript will only be build if necessary. - source_dir: str, optional - The source code directory. - build_cmd: str, optional - The npm command to build assets to the build_dir. - npm: str or list, optional. - The npm executable name, or a tuple of ['node', executable]. - """ - - class NPM(BaseCommand): - description = 'install package.json dependencies using npm' - - def run(self): - if skip_npm: - log.info('Skipping npm-installation') - return - node_package = path or os.path.abspath(os.getcwd()) - node_modules = pjoin(node_package, 'node_modules') - is_yarn = os.path.exists(pjoin(node_package, 'yarn.lock')) - - npm_cmd = npm - - if npm is None: - if is_yarn: - npm_cmd = ['yarn'] - else: - npm_cmd = ['npm'] - - if not which(npm_cmd[0]): - log.error("`{0}` unavailable. If you're running this command " - "using sudo, make sure `{0}` is available to sudo" - .format(npm_cmd[0])) - return - - if force or is_stale(node_modules, pjoin(node_package, 'package.json')): - log.info('Installing build dependencies with npm. This may ' - 'take a while...') - run(npm_cmd + ['install'], cwd=node_package) - if build_dir and source_dir and not force: - should_build = is_stale(build_dir, source_dir) - else: - should_build = True - if should_build: - run(npm_cmd + ['run', build_cmd], cwd=node_package) - - return NPM - - -def ensure_targets(targets): - """Return a Command that checks that certain files exist. - - Raises a ValueError if any of the files are missing. - - Note: The check is skipped if the `--skip-npm` flag is used. - """ - - class TargetsCheck(BaseCommand): - def run(self): - if skip_npm: - log.info('Skipping target checks') - return - missing = [t for t in targets if not os.path.exists(t)] - if missing: - raise ValueError(('missing files: %s' % missing)) - - return TargetsCheck - - -# `shutils.which` function copied verbatim from the Python-3.3 source. -def which(cmd, mode=os.F_OK | os.X_OK, path=None): - """Given a command, mode, and a PATH string, return the path which - conforms to the given mode on the PATH, or None if there is no such - file. - `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result - of os.environ.get("PATH"), or can be overridden with a custom search - path. - """ - - # Check that a given file can be accessed with the correct mode. - # Additionally check that `file` is not a directory, as on Windows - # directories pass the os.access check. - def _access_check(fn, mode): - return (os.path.exists(fn) and os.access(fn, mode) and - not os.path.isdir(fn)) - - # Short circuit. If we're given a full path which matches the mode - # and it exists, we're done here. - if _access_check(cmd, mode): - return cmd - - path = (path or os.environ.get("PATH", os.defpath)).split(os.pathsep) - - if sys.platform == "win32": - # The current directory takes precedence on Windows. - if os.curdir not in path: - path.insert(0, os.curdir) - - # PATHEXT is necessary to check on Windows. - pathext = os.environ.get("PATHEXT", "").split(os.pathsep) - # See if the given file matches any of the expected path extensions. - # This will allow us to short circuit when given "python.exe". - matches = [cmd for ext in pathext if cmd.lower().endswith(ext.lower())] - # If it does match, only test that one, otherwise we have to try - # others. - files = [cmd] if matches else [cmd + ext.lower() for ext in pathext] - else: - # On other platforms you don't have things like PATHEXT to tell you - # what file suffixes are executable, so just pass on cmd as-is. - files = [cmd] - - seen = set() - for dir in path: - dir = os.path.normcase(dir) - if dir not in seen: - seen.add(dir) - for thefile in files: - name = os.path.join(dir, thefile) - if _access_check(name, mode): - return name - return None - - -# --------------------------------------------------------------------------- -# Private Functions -# --------------------------------------------------------------------------- - - -def _wrap_command(cmds, cls, strict=True): - """Wrap a setup command - - Parameters - ---------- - cmds: list(str) - The names of the other commands to run prior to the command. - strict: boolean, optional - Whether to raise errors when a pre-command fails. - """ - class WrappedCommand(cls): - - def run(self): - if not getattr(self, 'uninstall', None): - try: - [self.run_command(cmd) for cmd in cmds] - except Exception: - if strict: - raise - else: - pass - # update package data - update_package_data(self.distribution) - - result = cls.run(self) - return result - return WrappedCommand - - -def _get_file_handler(package_data_spec, data_files_spec): - """Get a package_data and data_files handler command. - """ - class FileHandler(BaseCommand): - - def run(self): - package_data = self.distribution.package_data - package_spec = package_data_spec or dict() - - for (key, patterns) in package_spec.items(): - package_data[key] = _get_package_data(key, patterns) - - self.distribution.data_files = _get_data_files( - data_files_spec, self.distribution.data_files - ) - - return FileHandler - - -def _get_develop_handler(): - """Get a handler for the develop command""" - class _develop(develop): - - def install_for_development(self): - super(_develop, self).install_for_development() - self.run_command('handle_files') - for _, filenames in self.distribution.data_files: - for filename in filenames: - target = os.path.join(sys.prefix, filename) - self.mkpath(os.path.dirname(target)) - outf, copied = self.copy_file(filename, target) - - return _develop - - -def _glob_pjoin(*parts): - """Join paths for glob processing""" - if parts[0] in ('.', ''): - parts = parts[1:] - return pjoin(*parts).replace(os.sep, '/') - - -def _get_data_files(data_specs, existing, top=None): - """Expand data file specs into valid data files metadata. - - Parameters - ---------- - data_specs: list of tuples - See [create_cmdclass] for description. - existing: list of tuples - The existing distribution data_files metadata. - - Returns - ------- - A valid list of data_files items. - """ - if top is None: - top = os.path.abspath(os.getcwd()) - # Extract the existing data files into a staging object. - file_data = defaultdict(list) - for (path, files) in existing or []: - file_data[path] = files - - # Extract the files and assign them to the proper data - # files path. - for (path, dname, pattern) in data_specs or []: - if os.path.isabs(dname): - dname = os.path.relpath(dname, top) - - dname = dname.replace(os.sep, '/') - offset = 0 if dname in ('.', '') else len(dname) + 1 - files = _get_files(_glob_pjoin(dname, pattern), top=top) - - for fname in files: - # Normalize the path. - root = os.path.dirname(fname) - full_path = _glob_pjoin(path, root[offset:]) - if full_path.endswith('/'): - full_path = full_path[:-1] - file_data[full_path].append(fname) - - # Construct the data files spec. - data_files = [] - for (path, files) in file_data.items(): - data_files.append((path, files)) - return data_files - - -def _get_files(file_patterns, top=None): - """Expand file patterns to a list of paths. - - Parameters - ----------- - file_patterns: list or str - A list of glob patterns for the data file locations. - The globs can be recursive if they include a `**`. - They should be relative paths from the top directory or - absolute paths. - top: str - the directory to consider for data files - - Note: - Files in `node_modules` are ignored. - """ - if top is None: - top = os.path.abspath(os.getcwd()) - if not isinstance(file_patterns, (list, tuple)): - file_patterns = [file_patterns] - - for i, p in enumerate(file_patterns): - if os.path.isabs(p): - file_patterns[i] = os.path.relpath(p, top) - - matchers = [_compile_pattern(p) for p in file_patterns] - - files = set() - - for root, dirnames, filenames in os.walk(top): - # Don't recurse into node_modules - if 'node_modules' in dirnames: - dirnames.remove('node_modules') - for m in matchers: - for filename in filenames: - fn = os.path.relpath(_glob_pjoin(root, filename), top) - fn = fn.replace(os.sep, '/') - if m(fn): - files.add(fn) - - return list(files) - - -def _get_package_data(root, file_patterns=None): - """Expand file patterns to a list of `package_data` paths. - - Parameters - ----------- - root: str - The relative path to the package root from the current dir. - file_patterns: list or str, optional - A list of glob patterns for the data file locations. - The globs can be recursive if they include a `**`. - They should be relative paths from the root or - absolute paths. If not given, all files will be used. - - Note: - Files in `node_modules` are ignored. - """ - if file_patterns is None: - file_patterns = ['*'] - return _get_files(file_patterns, _glob_pjoin(os.path.abspath(os.getcwd()), root)) - - -def _compile_pattern(pat, ignore_case=True): - """Translate and compile a glob pattern to a regular expression matcher.""" - if isinstance(pat, bytes): - pat_str = pat.decode('ISO-8859-1') - res_str = _translate_glob(pat_str) - res = res_str.encode('ISO-8859-1') - else: - res = _translate_glob(pat) - flags = re.IGNORECASE if ignore_case else 0 - return re.compile(res, flags=flags).match - - -def _iexplode_path(path): - """Iterate over all the parts of a path. - - Splits path recursively with os.path.split(). - """ - (head, tail) = os.path.split(path) - if not head or (not tail and head == path): - if head: - yield head - if tail or not head: - yield tail - return - for p in _iexplode_path(head): - yield p - yield tail - - -def _translate_glob(pat): - """Translate a glob PATTERN to a regular expression.""" - translated_parts = [] - for part in _iexplode_path(pat): - translated_parts.append(_translate_glob_part(part)) - os_sep_class = '[%s]' % re.escape(SEPARATORS) - res = _join_translated(translated_parts, os_sep_class) - return '(?ms){res}\\Z'.format(res=res) - - -def _join_translated(translated_parts, os_sep_class): - """Join translated glob pattern parts. - - This is different from a simple join, as care need to be taken - to allow ** to match ZERO or more directories. - """ - res = '' - for part in translated_parts[:-1]: - if part == '.*': - # drop separator, since it is optional - # (** matches ZERO or more dirs) - res += part - else: - res += part + os_sep_class - - if translated_parts[-1] == '.*': - # Final part is ** - res += '.+' - # Follow stdlib/git convention of matching all sub files/directories: - res += '({os_sep_class}?.*)?'.format(os_sep_class=os_sep_class) - else: - res += translated_parts[-1] - return res - - -def _translate_glob_part(pat): - """Translate a glob PATTERN PART to a regular expression.""" - # Code modified from Python 3 standard lib fnmatch: - if pat == '**': - return '.*' - i, n = 0, len(pat) - res = [] - while i < n: - c = pat[i] - i = i + 1 - if c == '*': - # Match anything but path separators: - res.append('[^%s]*' % SEPARATORS) - elif c == '?': - res.append('[^%s]?' % SEPARATORS) - elif c == '[': - j = i - if j < n and pat[j] == '!': - j = j + 1 - if j < n and pat[j] == ']': - j = j + 1 - while j < n and pat[j] != ']': - j = j + 1 - if j >= n: - res.append('\\[') - else: - stuff = pat[i:j].replace('\\', '\\\\') - i = j + 1 - if stuff[0] == '!': - stuff = '^' + stuff[1:] - elif stuff[0] == '^': - stuff = '\\' + stuff - res.append('[%s]' % stuff) - else: - res.append(re.escape(c)) - return ''.join(res)