diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 640f0be2..00000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ - __init__.py export-subst -setup.py export-subst diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index a895370f..aed6a93d 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -19,8 +19,6 @@ env: PACKAGE: "geoviews" PYTHON_VERSION: "3.11" NODE_VERSION: "20" - MPLBACKEND: "Agg" - SETUPTOOLS_ENABLE_FEATURES: "legacy-editable" jobs: waiting_room: @@ -33,36 +31,29 @@ jobs: steps: - run: echo "All builds have finished, have been approved, and ready to publish" + pixi_lock: + name: Pixi lock + runs-on: ubuntu-latest + steps: + - uses: holoviz-dev/holoviz_tasks/pixi_lock@pixi + conda_build: name: Build Conda + needs: [pixi_lock] runs-on: "ubuntu-latest" steps: - - uses: actions/checkout@v4 - with: - fetch-depth: "100" - - name: Fetch unshallow - run: git fetch --prune --tags --unshallow -f - - uses: conda-incubator/setup-miniconda@v3 - with: - miniconda-version: "latest" - - uses: actions/setup-node@v4 + - uses: holoviz-dev/holoviz_tasks/pixi_install@pixi with: - node-version: ${{ env.NODE_VERSION }} - - name: conda setup - run: | - conda install -y conda-build build pyct + environments: "build" + install: false + download-data: false - name: conda build - run: | - source ./scripts/conda/build.sh - echo "CONDA_FILE="$CONDA_PREFIX/conda-bld/noarch/$PACKAGE-$VERSION-py_0.tar.bz2"" >> $GITHUB_ENV - echo "CONDA_CORE_FILE="$CONDA_PREFIX/conda-bld/noarch/$PACKAGE-core-$VERSION-py_0.tar.bz2"" >> $GITHUB_ENV + run: pixi run -e build build-conda - uses: actions/upload-artifact@v4 if: always() with: name: conda - path: | - ${{ env.CONDA_FILE }} - ${{ env.CONDA_CORE_FILE }} + path: dist/*.tar.bz2 if-no-files-found: error conda_publish: @@ -99,24 +90,16 @@ jobs: pip_build: name: Build PyPI + needs: [pixi_lock] runs-on: "ubuntu-latest" steps: - - uses: actions/checkout@v4 - with: - fetch-depth: "100" - - name: Fetch unshallow - run: git fetch --prune --tags --unshallow -f - - uses: actions/setup-python@v5 + - uses: holoviz-dev/holoviz_tasks/pixi_install@pixi with: - python-version: ${{ env.PYTHON_VERSION }} - - uses: actions/setup-node@v4 - with: - node-version: ${{ env.NODE_VERSION }} - - name: Install build - run: | - python -m pip install build + environments: "build" + install: false + download-data: false - name: Build package - run: python -m build . + run: pixi run -e build build-pip - uses: actions/upload-artifact@v4 if: always() with: @@ -160,27 +143,20 @@ jobs: npm_build: name: Build NPM + needs: [pixi_lock] runs-on: "ubuntu-latest" steps: - - uses: actions/checkout@v4 - with: - fetch-depth: "100" - - uses: actions/setup-python@v5 + - uses: holoviz-dev/holoviz_tasks/pixi_install@pixi with: - python-version: ${{ env.PYTHON_VERSION }} - - uses: actions/setup-node@v4 - with: - node-version: ${{ env.NODE_VERSION }} - - name: Fetch unshallow - run: git fetch --prune --tags --unshallow -f - - name: package install - run: | - python -m pip install -ve . + environments: "build" + download-data: false - name: npm build run: | - cd ${{ env.PACKAGE }} - TARBALL=$(npm pack .) + TARBALL=$(pixi run build-npm) echo "TARBALL=$TARBALL" >> $GITHUB_ENV + - name: npm publish dry-run + run: | + cd $PACKAGE npm publish --dry-run $TARBALL cd .. - uses: actions/upload-artifact@v4 diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 49aaa5c3..c0f28fb2 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -1,66 +1,77 @@ name: docs + on: push: tags: - - 'v[0-9]+.[0-9]+.[0-9]+' - - 'v[0-9]+.[0-9]+.[0-9]+a[0-9]+' - - 'v[0-9]+.[0-9]+.[0-9]+b[0-9]+' - - 'v[0-9]+.[0-9]+.[0-9]+rc[0-9]+' + - "v[0-9]+.[0-9]+.[0-9]+" + - "v[0-9]+.[0-9]+.[0-9]+a[0-9]+" + - "v[0-9]+.[0-9]+.[0-9]+b[0-9]+" + - "v[0-9]+.[0-9]+.[0-9]+rc[0-9]+" workflow_dispatch: inputs: target: - description: 'Site to build and deploy' + description: "Site to build and deploy" type: choice options: - - dev - - main - - dryrun + - dev + - main + - dryrun required: true default: dryrun schedule: - - cron: '0 18 * * SUN' + - cron: "0 18 * * SUN" + +defaults: + run: + shell: bash -el {0} + +env: + DISPLAY: ":99.0" jobs: - build_docs: - name: Documentation - runs-on: 'ubuntu-latest' - timeout-minutes: 120 - defaults: - run: - shell: bash -l {0} - env: - DESC: "Documentation build" - SETUPTOOLS_ENABLE_FEATURES: "legacy-editable" - MPLBACKEND: "Agg" - MOZ_HEADLESS: 1 - DISPLAY: ":99.0" - DASK_DATAFRAME__QUERY_PLANNING: false + pixi_lock: + name: Pixi lock + runs-on: ubuntu-latest steps: - - uses: holoviz-dev/holoviz_tasks/install@v0 + - uses: holoviz-dev/holoviz_tasks/pixi_lock@pixi + + docs_build: + name: Build Documentation + needs: [pixi_lock] + runs-on: "macos-latest" + timeout-minutes: 180 + outputs: + tag: ${{ steps.vars.outputs.tag }} + steps: + - uses: holoviz-dev/holoviz_tasks/pixi_install@pixi + with: + environments: docs + - name: Build documentation + run: pixi run -e docs docs-build + - uses: actions/upload-artifact@v4 + if: always() with: - name: Documentation - python-version: "3.10" - channels: pyviz,conda-forge,nodefaults - envs: "-o doc -o examples_extra" - cache: true - conda-update: true + name: docs + if-no-files-found: error + path: builtdocs - name: Set output id: vars run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT - - name: download data - run: | - conda activate test-environment - python download_data.py - - name: generate rst - run: | - conda activate test-environment - nbsite generate-rst --org holoviz --project-name geoviews - - name: build docs - run: | - conda activate test-environment - nbsite build --what=html --output=builtdocs --org holoviz --project-name geoviews + + docs_publish: + name: Publish Documentation + runs-on: "ubuntu-latest" + needs: [docs_build] + steps: + - uses: actions/download-artifact@v4 + with: + name: docs + path: builtdocs/ + - name: Set output + id: vars + run: echo "tag=${{ needs.docs_build.outputs.tag }}" >> $GITHUB_OUTPUT - name: Deploy dev - uses: peaceiris/actions-gh-pages@v3 + uses: peaceiris/actions-gh-pages@v4 if: | (github.event_name == 'workflow_dispatch' && github.event.inputs.target == 'dev') || (github.event_name == 'push' && (contains(steps.vars.outputs.tag, 'a') || contains(steps.vars.outputs.tag, 'b') || contains(steps.vars.outputs.tag, 'rc'))) @@ -73,7 +84,7 @@ jobs: if: | (github.event_name == 'workflow_dispatch' && github.event.inputs.target == 'main') || (github.event_name == 'push' && !(contains(steps.vars.outputs.tag, 'a') || contains(steps.vars.outputs.tag, 'b') || contains(steps.vars.outputs.tag, 'rc'))) - uses: peaceiris/actions-gh-pages@v3 + uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./builtdocs diff --git a/.github/workflows/nightly_lock.yaml b/.github/workflows/nightly_lock.yaml new file mode 100644 index 00000000..72c96929 --- /dev/null +++ b/.github/workflows/nightly_lock.yaml @@ -0,0 +1,25 @@ +name: nightly_lock +on: + workflow_dispatch: + schedule: + - cron: "0 0 * * *" + +env: + PACKAGE: "geoviews" + +jobs: + pixi_lock: + name: Pixi lock + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: holoviz-dev/holoviz_tasks/pixi_lock@pixi + - name: Upload lock-file to S3 + if: '!github.event.pull_request.head.repo.fork' + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_DEFAULT_REGION: "eu-west-1" + run: | + zip $(date +%Y-%m-%d).zip pixi.lock pixi.toml + aws s3 cp ./$(date +%Y-%m-%d).zip s3://assets.holoviz.org/lock/$PACKAGE/ diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 9da2fa5a..b07d0b83 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -20,6 +20,7 @@ on: description: "Use cache" type: boolean default: true + schedule: - cron: "0 18 * * SUN" @@ -27,24 +28,22 @@ concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true -env: - SETUPTOOLS_ENABLE_FEATURES: "legacy-editable" - DISPLAY: ":99.0" - PYTHONIOENCODING: "utf-8" - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PYDEVD_DISABLE_FILE_VALIDATION: 1 - USE_PYGEOS: 0 - defaults: run: shell: bash -el {0} +env: + DISPLAY: ":99.0" + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COV: "--cov=./geoviews --cov-report=xml" + jobs: pre_commit: name: Run pre-commit runs-on: "ubuntu-latest" steps: - uses: holoviz-dev/holoviz_tasks/pre-commit@v0 + setup: name: Setup workflow runs-on: ubuntu-latest @@ -54,7 +53,7 @@ jobs: code_change: ${{ steps.filter.outputs.code }} matrix: ${{ env.MATRIX }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 if: github.event_name != 'pull_request' - name: Check for code changes uses: dorny/paths-filter@v3 @@ -84,7 +83,7 @@ jobs: run: | MATRIX=$(jq -nsc '{ "os": ["ubuntu-latest", "macos-latest", "windows-latest"], - "python-version": ["3.9", "3.11"] + "environment": ["test-310", "test-312"], }') echo "MATRIX=$MATRIX" >> $GITHUB_ENV - name: Set test matrix with 'full' option @@ -92,7 +91,7 @@ jobs: run: | MATRIX=$(jq -nsc '{ "os": ["ubuntu-latest", "macos-latest", "windows-latest"], - "python-version": ["3.9", "3.10", "3.11"] + "environment": ["test-310", "test-311", "test-312"] }') echo "MATRIX=$MATRIX" >> $GITHUB_ENV - name: Set test matrix with 'downstream' option @@ -100,97 +99,60 @@ jobs: run: | MATRIX=$(jq -nsc '{ "os": ["ubuntu-latest"], - "python-version": ["3.11"] + "environment": ["test-311"] }') echo "MATRIX=$MATRIX" >> $GITHUB_ENV + pixi_lock: + name: Pixi lock + runs-on: ubuntu-latest + steps: + - uses: holoviz-dev/holoviz_tasks/pixi_lock@pixi + with: + cache: ${{ github.event.inputs.cache == 'true' || github.event.inputs.cache == '' }} + unit_test_suite: - name: Unit tests on Python ${{ matrix.python-version }}, ${{ matrix.os }} - needs: [pre_commit, setup] + name: unit:${{ matrix.environment }}:${{ matrix.os }} + needs: [pre_commit, setup, pixi_lock] runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: ${{ fromJson(needs.setup.outputs.matrix) }} - timeout-minutes: 60 + timeout-minutes: 120 steps: - - uses: holoviz-dev/holoviz_tasks/install@v0 + - uses: holoviz-dev/holoviz_tasks/pixi_install@pixi if: needs.setup.outputs.code_change == 'true' with: - name: unit_test_suite - python-version: ${{ matrix.python-version }} - channel-priority: strict - channels: pyviz/label/dev,bokeh,conda-forge,nodefaults - envs: "-o tests -o examples_extra -o recommended -o tests_ci" - cache: ${{ github.event.inputs.cache || github.event.inputs.cache == '' }} - conda-update: true - nodejs: true - id: install - - name: download data - if: needs.setup.outputs.code_change == 'true' - run: | - conda activate test-environment - python scripts/download_data.py - - name: git describe + environments: ${{ matrix.environment }} + - name: Test Unit if: needs.setup.outputs.code_change == 'true' run: | - git describe - - name: doit test_unit + pixi run -e ${{ matrix.environment }} test-unit $COV + - name: Test Examples if: needs.setup.outputs.code_change == 'true' run: | - conda activate test-environment - doit test_unit - - name: test examples + pixi run -e ${{ matrix.environment }} test-example + - uses: codecov/codecov-action@v4 if: needs.setup.outputs.code_change == 'true' - run: | - conda activate test-environment - doit test_examples - - name: codecov - if: needs.setup.outputs.code_change == 'true' - run: | - conda activate test-environment - codecov + with: + token: ${{ secrets.CODECOV_TOKEN }} core_test_suite: - name: Core tests on Python ${{ matrix.python-version }}, ${{ matrix.os }} - needs: [pre_commit] + name: core:${{ matrix.environment }}:${{ matrix.os }} + needs: [pre_commit, setup, pixi_lock] runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: ["ubuntu-latest"] - python-version: ["3.12"] + environment: ["test-core"] timeout-minutes: 120 steps: - # Add back when this works on Python 3.12 - # - uses: holoviz-dev/holoviz_tasks/install@v0 - # with: - # name: core_test_suite - # python-version: ${{ matrix.python-version }} - # # channel-priority: strict - # channels: pyviz/label/dev,conda-forge,nodefaults - # envs: "-o tests_core -o tests_ci" - # cache: ${{ github.event.inputs.cache || github.event.inputs.cache == '' }} - # conda-update: true - # id: install - - uses: actions/checkout@v3 - with: - fetch-depth: "100" - - name: Fetch unshallow - run: git fetch --prune --tags --unshallow -f - - uses: actions/setup-node@v3 - with: - node-version: 20.x - - uses: actions/setup-python@v4 + - uses: holoviz-dev/holoviz_tasks/pixi_install@pixi + if: needs.setup.outputs.code_change == 'true' with: - python-version: "3.12" - - name: install - run: | - python -m pip install -ve '.[tests_core, tests_ci]' - - name: download data - run: | - # conda activate test-environment - python scripts/download_data.py - - name: doit test_unit + environments: ${{ matrix.environment }} + - name: Test Unit + if: needs.setup.outputs.code_change == 'true' run: | - # conda activate test-environment - pytest geoviews + pixi run -e ${{ matrix.environment }} test-unit diff --git a/.gitignore b/.gitignore index 4784075e..960f8ba6 100644 --- a/.gitignore +++ b/.gitignore @@ -81,3 +81,8 @@ geoviews/.version node_modules/ .bokeh examples/data + +# Pixi + hatch +pixi.lock +.pixi +geoviews/_version.py diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e69de29b..00000000 diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 94ab9353..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,13 +0,0 @@ -include LICENSE -include README.md -include geoviews/.version -include geoviews/models/*.ts -include geoviews/icons/*.png -include geoviews/*.json -include geoviews/index.ts -global-exclude *.py[co] -global-exclude *~ -global-exclude *.ipynb_checkpoints/* -graft examples -graft geoviews/examples -graft geoviews/dist diff --git a/doc/_static/custom.css b/doc/_static/custom.css index 86b9d179..0b8b44f9 100644 --- a/doc/_static/custom.css +++ b/doc/_static/custom.css @@ -1,4 +1,27 @@ +:root[data-theme="light"], +:root[data-theme="dark"] { + --pst-color-inline-code: var(--holoviz-main-color); + --pst-color-link-hover: var(--holoviz-main-color); + --pst-color-link: var(--holoviz-main-color); + --pst-color-primary: var(--holoviz-main-color); + --pst-color-secondary-highlight: var(--holoviz-main-color); + --pst-color-secondary: var(--holoviz-main-color); + --pst-violet-600: var(--holoviz-main-color); /* back to top hover */ + --sd-color-card-border-hover: var(--holoviz-main-color); +} + :root[data-theme="light"] { - --pst-color-primary: rgb(47, 47, 47); - --pst-color-link: rgb(56, 123, 178); + --holoviz-main-color: #387bb2; +} + +:root[data-theme="dark"] { + --holoviz-main-color: #387bb2; +} + +#binder-link { + display: inline-block; + font-size: 0.9rem; + padding-left: 1.5rem; + padding-top: 1rem; + padding-bottom: 1rem; } diff --git a/doc/conf.py b/doc/conf.py index e50264d3..932eac26 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -46,6 +46,9 @@ 'nbsite.gallery', 'nbsite.analytics', ] + +myst_enable_extensions = ["colon_fence", "deflist"] + napoleon_numpy_docstring = True nbsite_analytics = { diff --git a/doc/developer_guide/index.md b/doc/developer_guide/index.md new file mode 100644 index 00000000..a252095f --- /dev/null +++ b/doc/developer_guide/index.md @@ -0,0 +1,185 @@ +# Setting up a development environment + +The GeoViews library is a project that provides a wide range of data interfaces and an extensible set of plotting backends, which means the development and testing process involves a broad set of libraries. + +This guide describes how to install and configure development environments. + +If you have any problems with the steps here, please reach out in the `dev` channel on [Discord](https://discord.gg/rb6gPXbdAr) or on [Discourse](https://discourse.holoviz.org/). + +## Preliminaries + +### Basic understanding of how to contribute to Open Source + +If this is your first open-source contribution, please study one +or more of the below resources. + +- [How to Get Started with Contributing to Open Source | Video](https://youtu.be/RGd5cOXpCQw) +- [Contributing to Open-Source Projects as a New Python Developer | Video](https://youtu.be/jTTf4oLkvaM) +- [How to Contribute to an Open Source Python Project | Blog post](https://www.educative.io/blog/contribue-open-source-python-project) + +### Git + +The GeoViews source code is stored in a [Git](https://git-scm.com) source control repository. The first step to working on GeoViews is to install Git onto your system. There are different ways to do this, depending on whether you use Windows, Mac, or Linux. + +To install Git on any platform, refer to the [Installing Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) section of the [Pro Git Book](https://git-scm.com/book/en/v2). + +To contribute to GeoViews, you will also need [Github account](https://github.com/join) and knowledge of the [_fork and pull request workflow_](https://docs.github.com/en/get-started/quickstart/contributing-to-projects). + +### Pixi + +Developing all aspects of GeoViews requires a wide range of packages in different environments. To make this more manageable, Pixi manages the developer experience. To install Pixi, follow [this guide](https://pixi.sh/latest/#installation). + +#### Glossary + +- Tasks: A task is what can be run with `pixi run `. Tasks can be anything from installing packages to running tests. +- Environments: An environment is a set of packages installed in a virtual environment. Each environment has a name; you can run tasks in a specific environment with the `-e` flag. For example, `pixi run -e test-core test-unit` will run the `test-unit` task in the `test-core` environment. +- Lock-file: A lock-file is a file that contains all the information about the environments. + +For more information, see the [Pixi documentation](https://pixi.sh/latest/). + +:::{admonition} Note +:class: info + +The first time you run `pixi`, it will create a `.pixi` directory in the source directory. +This directory will contain all the files needed for the virtual environments. +The `.pixi` directory can be large, so it is advised not to put the source directory into a cloud-synced directory. +::: + +## Installing the Project + +### Cloning the Project + +The source code for the GeoViews project is hosted on [GitHub](https://github.com/holoviz/geoviews). The first thing you need to do is clone the repository. + +1. Go to [github.com/holoviz/geoviews](https://github.com/holoviz/geoviews) +2. [Fork the repository](https://docs.github.com/en/get-started/quickstart/contributing-to-projects#forking-a-repository) +3. Run in your terminal: `git clone https://github.com//geoviews` + +The instructions for cloning above created a `geoviews` directory at your file system location. +This `geoviews` directory is the _source checkout_ for the remainder of this document, and your current working directory is this directory. + +## Start developing + +To start developing, run the following command + +```bash +pixi install +``` + +The first time you run it, it will create a `pixi.lock` file with information for all available environments. This command will take a minute or so to run. +When this is finished, it is possible to run the following command to download the data GeoViews tests and examples depend upon. + +```bash +pixi run -e download-data download-data +``` + +All available tasks can be found by running `pixi task list`, the following sections will give a brief introduction to the most common tasks. + +### Syncing Git tags with upstream repository + +If you are working from a forked repository of GeoViews, you will need to sync the tags with the upstream repo. +This is needed because the GeoViews version number depends on [`git tags`](https://git-scm.com/book/en/v2/Git-Basics-Tagging). +Syncing the git tagsĀ can be done with: + +```bash +pixi run sync-git-tags +``` + +### Editable install + +It can be advantageous to install the GeoViews in [editable mode](https://pip.pypa.io/en/stable/topics/local-project-installs/#editable-installs): + +```bash +pixi run install +``` + +:::{admonition} Note +:class: info + +Currently, this needs to be run for each environment. So, if you want to install in the `test-ui` environment, you can add `--environment` / `-e` to the command: + +```bash +pixi run -e test-ui install +``` + +::: + +## Linting + +GeoViews uses [pre-commit](https://pre-commit.com/) to apply linting to GeoViews code. Linting can be run for all the files with: + +```bash +pixi run lint +``` + +Linting can also be set up to run automatically with each commit; this is the recommended way because if linting is not passing, the [Continuous Integration](https://en.wikipedia.org/wiki/Continuous_integration) (CI) will also fail. + +```bash +pixi run lint-install +``` + +## Testing + +To help keep GeoViews maintainable, all Pull Requests (PR) with code changes should typically be accompanied by relevant tests. While exceptions may be made for specific circumstances, the default assumption should be that a Pull Request without tests will not be merged. + +There are three types of tasks and five environments related to tests. + +### Unit tests + +Unit tests are usually small tests executed with [pytest](https://docs.pytest.org). They can be found in `geoviews/tests/`. +Unit tests can be run with the `test-unit` task: + +```bash +pixi run test-unit +``` + +The task is available in the following environments: `test-310`, `test-311`, `test-312`, and `test-core`. Where the first ones have the same environments except for different Python versions, and `test-core` only has a core set of dependencies. + +If you haven't set the environment flag in the command, a menu will help you select which one of the environments to use. + +### Example tests + +GeoViews's documentation consists mainly of Jupyter Notebooks. The example tests execute all the notebooks and fail if an error is raised. Example tests are possible thanks to [nbval](https://nbval.readthedocs.io/) and can be found in the `examples/` folder. +Example tests can be run with the following command: + +```bash +pixi run test-example +``` + +This task has the same environments as the unit tests except for `test-core`. + +## Documentation + +The documentation can be built with the command: + +```bash +pixi run docs-build +``` + +As GeoViews uses notebooks for much of the documentation, this will take significant time to run (around an hour). + +A development version of GeoViews can be found [here](https://dev.geoviews.org/). You can ask a maintainer if they want to make a dev release for your PR, but there is no guarantee they will say yes. + +## Build + +GeoViews have three build tasks. One is for building packages for Pip, Conda, and NPM. + +```bash +pixi run build-pip +pixi run build-conda +pixi run build-npm +``` + +## Continuous Integration + +Every push to the `main` branch or any PR branch on GitHub automatically triggers a test build with [GitHub Actions](https://github.com/features/actions). + +You can see the list of all current and previous builds at [this URL](https://github.com/holoviz/geoviews/actions) + +### Etiquette + +GitHub Actions provides free build workers for open-source projects. A few considerations will help you be considerate of others needing these limited resources: + +- Run the tests locally before opening or pushing to an opened PR. + +- Group commits to meaningful chunks of work before pushing to GitHub (i.e., don't push on every commit). diff --git a/doc/index.rst b/doc/index.rst index 9b8f990a..ad2d37ad 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -96,5 +96,6 @@ or have set up your system to be able to build them. User Guide Gallery Topics + Developer Guide Releases About diff --git a/dodo.py b/dodo.py deleted file mode 100644 index 7afc54be..00000000 --- a/dodo.py +++ /dev/null @@ -1,15 +0,0 @@ -import os -if "PYCTDEV_ECOSYSTEM" not in os.environ: - os.environ["PYCTDEV_ECOSYSTEM"] = "conda" - -from pyctdev import * # noqa: api - - -def task_pip_on_conda(): - """Experimental: provide pip build env via conda""" - return {'actions':[ - # some ecosystem=pip build tools must be installed with conda when using conda... - 'conda install -y pip twine wheel rfc3986 keyring', - # ..and some are only available via conda-forge - 'conda install -y -c conda-forge tox virtualenv' - ]} diff --git a/geoviews/__init__.py b/geoviews/__init__.py index 96151cf4..ee73c4a7 100644 --- a/geoviews/__init__.py +++ b/geoviews/__init__.py @@ -1,6 +1,5 @@ from functools import partial -import param from holoviews import ( Cycle, Dimension, @@ -23,12 +22,8 @@ save, ) -from . import ( - data, - feature, - plotting, - tile_sources, -) +from . import data, feature, plotting, tile_sources +from .__version import __version__ from ._warnings import GeoviewsDeprecationWarning, GeoviewsUserWarning from .element import ( RGB, @@ -60,12 +55,6 @@ ) from .util import from_xarray -__version__ = str( - param.version.Version( - fpath=__file__, archive_commit="$Format:%h$", reponame="geoviews" - ) -) - __all__ = ( "Contours", "Cycle", diff --git a/geoviews/__version.py b/geoviews/__version.py new file mode 100644 index 00000000..c42d1b46 --- /dev/null +++ b/geoviews/__version.py @@ -0,0 +1,44 @@ +"""Define the package version. + +Called __version.py as setuptools_scm will create a _version.py +""" + +import os.path + +PACKAGE = "geoviews" + +try: + # For performance reasons on imports, avoid importing setuptools_scm + # if not in a .git folder + if os.path.exists(os.path.join(os.path.dirname(__file__), "..", ".git")): + # If setuptools_scm is installed (e.g. in a development environment with + # an editable install), then use it to determine the version dynamically. + from setuptools_scm import get_version + + # This will fail with LookupError if the package is not installed in + # editable mode or if Git is not installed. + __version__ = get_version(root="..", relative_to=__file__) + else: + raise FileNotFoundError +except (ImportError, LookupError, FileNotFoundError): + # As a fallback, use the version that is hard-coded in the file. + try: + # __version__ was added in _version in setuptools-scm 7.0.0, we rely on + # the hopefully stable version variable. + from ._version import version as __version__ + except (ModuleNotFoundError, ImportError): + # Either _version doesn't exist (ModuleNotFoundError) or version isn't + # in _version (ImportError). ModuleNotFoundError is a subclass of + # ImportError, let's be explicit anyway. + + # Try something else: + from importlib.metadata import PackageNotFoundError, version + + try: + __version__ = version(PACKAGE) + except PackageNotFoundError: + # The user is probably trying to run this without having installed + # the package. + __version__ = "0.0.0+unknown" + +__all__ = ("__version__",) diff --git a/geoviews/tests/data/test_iris.py b/geoviews/tests/data/test_iris.py index bd2ef350..d2e87a61 100644 --- a/geoviews/tests/data/test_iris.py +++ b/geoviews/tests/data/test_iris.py @@ -4,8 +4,6 @@ try: import iris - from iris.exceptions import MergeError - from iris.tests.stock import lat_lon_cube except ImportError: raise SkipTest("Could not import iris, skipping IrisInterface tests.") from None @@ -14,6 +12,8 @@ from holoviews.element import Image from holoviews.tests.core.data.test_gridinterface import BaseGridInterfaceTests from holoviews.tests.core.data.test_imageinterface import BaseImageElementInterfaceTests +from iris.exceptions import MergeError +from iris.tests.stock import lat_lon_cube from geoviews.data.iris import coord_to_dimension diff --git a/geoviews/tests/plotting/bokeh/test_bokeh_chart.py b/geoviews/tests/plotting/bokeh/test_bokeh_chart.py index 1dca301b..9c0b55c3 100644 --- a/geoviews/tests/plotting/bokeh/test_bokeh_chart.py +++ b/geoviews/tests/plotting/bokeh/test_bokeh_chart.py @@ -1,5 +1,5 @@ - import numpy as np +import pytest import geoviews as gv @@ -7,6 +7,8 @@ class TestImageStackPlot: def test_image_stack_crs(self): + pytest.importorskip("scipy") + x = np.arange(-120, -115) y = np.arange(40, 43) a = np.random.rand(len(y), len(x)) diff --git a/geoviews/tests/plotting/mpl/test_chart.py b/geoviews/tests/plotting/mpl/test_chart.py index 5843505c..ac6cca27 100644 --- a/geoviews/tests/plotting/mpl/test_chart.py +++ b/geoviews/tests/plotting/mpl/test_chart.py @@ -1,6 +1,5 @@ import numpy as np import pytest -import xarray as xr from holoviews.tests.plotting.utils import ParamLogStream from test_plot import TestMPLPlot @@ -33,6 +32,8 @@ def test_windbarbs(self): np.testing.assert_almost_equal(mpl_barbs.v.data, V.T.flatten()) def test_windbarbs_dataset(self): + xr = pytest.importorskip("xarray") + x = np.linspace(-1, 1, 4) X, Y = np.meshgrid(x, x) U, V = 10 * X, 1 * Y @@ -71,6 +72,8 @@ def test_windbarbs_from_uv(self): np.testing.assert_almost_equal(gv_barbs.data["Magnitude"].T.flatten(), gv_barbs_uv.data["Magnitude"]) def test_windbarbs_dataset_from_uv_other_dim(self): + xr = pytest.importorskip("xarray") + x = np.linspace(-1, 1, 4) X, Y = np.meshgrid(x, x) U, V = 10 * X, 3 * Y diff --git a/geoviews/tests/test_operation.py b/geoviews/tests/test_operation.py index 044acc6a..71757eca 100644 --- a/geoviews/tests/test_operation.py +++ b/geoviews/tests/test_operation.py @@ -1,9 +1,10 @@ import pytest -import xarray as xr from holoviews.operation import contours import geoviews as gv +xr = pytest.importorskip("xarray") + @pytest.mark.filterwarnings( "ignore:numpy.ndarray size changed, may indicate binary incompatibility" # https://github.com/pydata/xarray/issues/7259 diff --git a/geoviews/tests/test_projection.py b/geoviews/tests/test_projection.py index dcc7dc29..599fb30b 100644 --- a/geoviews/tests/test_projection.py +++ b/geoviews/tests/test_projection.py @@ -1,5 +1,6 @@ import cartopy.crs as ccrs import numpy as np +import pytest from geoviews.element import Image, VectorField, WindBarbs from geoviews.element.comparison import ComparisonTestCase @@ -9,6 +10,7 @@ class TestProjection(ComparisonTestCase): def test_image_latlon360_wrapping(self): + pytest.importorskip("scipy") xs = np.linspace(72, 360, 5) ys = np.linspace(-60, 60, 3) img = Image((xs, ys, xs[np.newaxis, :]*ys[:, np.newaxis])) @@ -21,6 +23,7 @@ def test_image_latlon360_wrapping(self): ])) def test_image_project_latlon_to_mercator(self): + pytest.importorskip("scipy") xs = np.linspace(72, 360, 5) ys = np.linspace(-60, 60, 3) img = Image((xs, ys, xs[np.newaxis, :]*ys[:, np.newaxis])) diff --git a/hatch_build.py b/hatch_build.py new file mode 100644 index 00000000..3f73daeb --- /dev/null +++ b/hatch_build.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +import json +import os +import sys +import typing as t +from pathlib import Path + +from hatchling.builders.hooks.plugin.interface import BuildHookInterface + +BASE_DIR = Path(__file__).parent +GREEN, RED, RESET = "\033[0;32m", "\033[0;31m", "\033[0m" + + +def build_models(): + from bokeh.ext import build + + print(f"{GREEN}[GEOVIEWS]{RESET} Starting building custom models", flush=True) + geoviews_dir = BASE_DIR / "geoviews" + success = build(geoviews_dir) + if sys.platform != "win32": + # npm can cause non-blocking stdout; so reset it just in case + import fcntl + + flags = fcntl.fcntl(sys.stdout, fcntl.F_GETFL) + fcntl.fcntl(sys.stdout, fcntl.F_SETFL, flags & ~os.O_NONBLOCK) + + if success: + print(f"{GREEN}[GEOVIEWS]{RESET} Finished building custom models", flush=True) + else: + print(f"{RED}[GEOVIEWS]{RESET} Failed building custom models", flush=True) + sys.exit(1) + + +def clean_js_version(version): + version = version.replace("-", "") + for dev in ("a", "b", "rc"): + version = version.replace(dev + ".", dev) + return version + + +def validate_js_version(version): + # TODO: Double check the logic in this function + version = version.split(".post")[0] + with open("./geoviews/package.json") as f: + package_json = json.load(f) + js_version = package_json["version"] + version = version.split("+")[0] + if any(dev in version for dev in ("a", "b", "rc")) and "-" not in js_version: + raise ValueError( + f"geoviews.js dev versions ({js_version}) must separate dev suffix with a dash, e.g. v1.0.0rc1 should be v1.0.0-rc.1." + ) + if version != "None" and version != clean_js_version(js_version): + raise ValueError( + f"geoviews.js version ({js_version}) does not match geoviews version ({version}). Cannot build release." + ) + + +class BuildHook(BuildHookInterface): + """The hatch build hook.""" + + PLUGIN_NAME = "install" + + def initialize(self, version: str, build_data: dict[str, t.Any]) -> None: + """Initialize the plugin.""" + if self.target_name not in ["wheel", "sdist"]: + return + + validate_js_version(self.metadata.version) + build_models() diff --git a/pixi.toml b/pixi.toml new file mode 100644 index 00000000..c6b72971 --- /dev/null +++ b/pixi.toml @@ -0,0 +1,150 @@ +[project] +name = "geoviews" +channels = ["pyviz/label/dev", "conda-forge"] +platforms = ["linux-64", "osx-arm64", "osx-64", "win-64"] + +[tasks] +install = 'python -m pip install --no-deps --disable-pip-version-check -e .' +sync-git-tags = 'python scripts/sync_git_tags.py geoviews' + +[activation.env] +PYTHONIOENCODING = "utf-8" +USE_PYGEOS = "0" +DASK_DATAFRAME__QUERY_PLANNING = "False" + +[environments] +test-310 = ["py310", "test-core", "test", "example", "test-example", "download-data"] +test-311 = ["py311", "test-core", "test", "example", "test-example", "download-data"] +test-312 = ["py312", "test-core", "test", "example", "test-example", "download-data"] +test-core = ["py312", "test-core"] +docs = ["py311", "example", "doc", "download-data"] +build = ["py311", "build"] +lint = ["py311", "lint"] +download-data = ["download-data"] + +[dependencies] +bokeh = ">=3.4.0,<3.5.0" +cartopy = ">=0.18.0" +holoviews = ">=1.16.0" +nodejs = "20.9.*" # 20.12.2 breaks bokeh build on Windows: https://github.com/bokeh/bokeh/pull/13851, removed with Bokeh 3.5 +numpy = "*" +packaging = "*" +panel = ">=1.0.0" +param = "*" +pip = "*" +pyproj = "*" +shapely = "*" +xyzservices = "*" + +[feature.py310.dependencies] +python = "3.10.*" + +[feature.py311.dependencies] +python = "3.11.*" + +[feature.py312.dependencies] +python = "3.12.*" + +[feature.example.dependencies] +datashader = "*" +fiona = "*" +geopandas-base = "*" +iris = ">=3.5" +matplotlib-base = ">2.2" +mock = "*" +netcdf4 = "*" +pandas = "*" +scipy = "*" +shapely = "*" +xarray = "*" +xesmf = "*" + +[feature.download-data.tasks] +download-data = 'python scripts/download_data.py' + +[feature.download-data.dependencies] +cftime = "*" +geodatasets = "*" +pooch = "*" +pyct = "*" +scipy = "*" +setuptools = "*" # Because of pyct +xarray = "*" + +# ============================================= +# =================== TESTS =================== +# ============================================= +[feature.test-core.dependencies] +psutil = "*" +pytest = "*" +pytest-cov = "*" +pytest-github-actions-annotate-failures = "*" +pytest-xdist = "*" + +[feature.test-core.tasks] +test-unit = 'pytest geoviews/tests -n logical --dist loadgroup' + +[feature.test.dependencies] +cftime = "*" +datashader = "*" +filelock = "*" +fiona = "*" +geopandas-base = "*" +iris = ">=3.5" +matplotlib-base = ">2.2" +nbval = "*" +netcdf4 = "*" +pandas = "*" +pyviz_comms = "*" +rioxarray = "*" +scipy = "*" +shapely = "*" +xarray = "*" + +[feature.test-example.tasks] +test-example = 'pytest -n logical --dist loadscope --nbval-lax examples' + +[feature.test-example.dependencies] +nbval = "*" + +# ============================================= +# =================== DOCS ==================== +# ============================================= +[feature.doc.dependencies] +graphviz = "*" +lxml = "*" +nbsite = ">=0.8.4,<0.9.0" +selenium = "*" + +[feature.doc.activation.env] +MOZ_HEADLESS = "1" +MPLBACKEND = "Agg" + +[feature.doc.tasks] +_docs-generate-rst = 'nbsite generate-rst --org holoviz --project-name geoviews' +_docs-generate = 'nbsite build --what=html --output=builtdocs --org holoviz --project-name geoviews' + +[feature.doc.tasks.docs-build] +depends_on = ['_docs-generate-rst', '_docs-generate'] + +# ============================================= +# ================== BUILD ==================== +# ============================================= +[feature.build.dependencies] +python-build = "*" +conda-build = "*" + +[feature.build.tasks] +build-conda = 'bash scripts/conda/build.sh' +build-pip = 'python -m build .' +build-npm = { cmd = "npm pack .", cwd = "geoviews" } + +# ============================================= +# =================== LINT ==================== +# ============================================= +[feature.lint.dependencies] +pre-commit = "*" + +[feature.lint.tasks] +lint = 'pre-commit run --all-files' +lint-install = 'pre-commit install' diff --git a/pyproject.toml b/pyproject.toml index 4e1c7995..40625a1d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,11 +1,89 @@ [build-system] -requires = [ - "param >=1.9.2", - "pyct >=0.4.4", - "bokeh ==3.4", - "setuptools", +requires = ["hatchling", "hatch-vcs", 'bokeh ==3.4'] +build-backend = "hatchling.build" + +[project] +name = "geoviews" +dynamic = ["version"] +description = 'GeoViews is a Python library that makes it easy to explore and visualize geographical, meteorological, and oceanographic datasets, such as those used in weather, climate, and remote sensing research.' +readme = "README.md" +license = { text = "BSD 3-Clause" } +requires-python = ">=3.10" +authors = [{ name = "HoloViz developers", email = "developers@holoviz.org" }] +maintainers = [{ name = "HoloViz developers", email = "developers@holoviz.org" }] +classifiers = [ + "License :: OSI Approved :: BSD License", + "Development Status :: 5 - Production/Stable", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Operating System :: OS Independent", + "Intended Audience :: Science/Research", + "Intended Audience :: Developers", + "Natural Language :: English", + "Framework :: Matplotlib", + "Topic :: Scientific/Engineering", + "Topic :: Software Development :: Libraries", +] + +dependencies = [ + 'bokeh >=3.4.0,<3.5.0', + 'cartopy >=0.18.0', + 'holoviews >=1.16.0', + 'numpy', + 'packaging', + 'panel >=1.0.0', + 'param', + 'pyproj', + 'shapely', + 'xyzservices', +] + +[project.urls] +Homepage = "https://geoviews.org" +Source = "https://github.com/holoviz/geoviews" +HoloViz = "https://holoviz.org/" + +[project.optional-dependencies] +recommended = [ + 'datashader', + 'geopandas', + 'matplotlib >2.2', + 'netcdf4', + 'pandas', + 'pooch', + 'pyct', + 'scipy', + 'shapely', + 'xarray', ] -build-backend = "setuptools.build_meta" +tests = ['psutil', 'pytest', 'pytest-xdist'] + +[project.scripts] +geoviews = "geoviews.command:main" + +[tool.hatch.version] +source = "vcs" +raw-options = { version_scheme = "no-guess-dev" } + +[tool.hatch.build.targets.wheel] +include = ["geoviews"] + +[tool.hatch.build.targets.wheel.force-include] +"geoviews/dist" = "geoviews/dist" + +[tool.hatch.build.targets.sdist] +include = ["geoviews", "scripts"] + +[tool.hatch.build.targets.sdist.force-include] +"geoviews/dist" = "geoviews/dist" +examples = "geoviews/examples" + +[tool.hatch.build.hooks.vcs] +version-file = "geoviews/_version.py" + +[tool.hatch.build.hooks.custom] +path = 'hatch_build.py' [tool.ruff] fix = true @@ -30,20 +108,22 @@ select = [ ] ignore = [ - "E402", # Module level import not at top of file - "E501", # Line too long - "E701", # Multiple statements on one line - "E712", # Comparison to true should be is - "E731", # Do not assign a lambda expression, use a def - "E741", # Ambiguous variable name - "F405", # From star imports - "PLE0604", # Invalid object in `__all__`, must contain only strings - "PLE0605", # Invalid format for `__all__` - "PLR091", # Too many arguments/branches/statements - "PLR2004", # Magic value used in comparison - "PLW2901", # `for` loop variable is overwritten - "RUF005", # Consider {expr} instead of concatenation - "RUF012", # Mutable class attributes should use `typing.ClassVar` + "E402", # Module level import not at top of file + "E501", # Line too long + "E701", # Multiple statements on one line + "E712", # Comparison to true should be is + "E731", # Do not assign a lambda expression, use a def + "E741", # Ambiguous variable name + "F405", # From star imports + "PLE0604", # Invalid object in `__all__`, must contain only strings + "PLE0605", # Invalid format for `__all__` + "PLR091", # Too many arguments/branches/statements + "PLR2004", # Magic value used in comparison + "PLW2901", # `for` loop variable is overwritten + "RUF005", # Consider {expr} instead of concatenation + "RUF012", # Mutable class attributes should use `typing.ClassVar` + 'B905', # `zip()` without an explicit `strict=` parameter + 'UP038', # Use `X | Y` in `isinstance` call instead of `(X, Y)` ] extend-unsafe-fixes = [ @@ -54,8 +134,10 @@ extend-unsafe-fixes = [ [tool.ruff.lint.per-file-ignores] "__init__.py" = ["F403"] "geoviews/tests/*" = [ - "RUF001", "RUF002", "RUF003", # Ambiguous unicode character - "NPY002", # Replace legacy `np.random.rand` call with Generator + "RUF001", # Ambiguous unicode character + "RUF002", # Ambiguous unicode character + "RUF003", # Ambiguous unicode character + "NPY002", # Replace legacy `np.random.rand` call with Generator ] [tool.ruff.lint.isort] @@ -68,7 +150,13 @@ skip = "doc/generate_modules.py,*.json,*.csv" write-changes = true [tool.pytest.ini_options] -addopts = ["--pyargs", "--doctest-ignore-import-errors", "--strict-config", "--strict-markers", "--color=yes"] +addopts = [ + "--pyargs", + "--doctest-ignore-import-errors", + "--strict-config", + "--strict-markers", + "--color=yes", +] norecursedirs = "doc .git dist build _build .ipynb_checkpoints" minversion = 7 xfail_strict = true @@ -84,12 +172,12 @@ filterwarnings = [ # 2023-09: See https://github.com/Unidata/MetPy/pull/3117 "ignore:'xdrlib' is deprecated and slated for removal in Python 3.13:DeprecationWarning:metpy.io.nexrad", # 2024-01 - "ignore:\\s*Pyarrow will become a required dependency of pandas:DeprecationWarning", # Will go away by itself in Pandas 3.0 - "ignore:Passing a (SingleBlockManager|BlockManager) to (Series|GeoSeries|DataFrame|GeoDataFrame) is deprecated:DeprecationWarning", # https://github.com/holoviz/spatialpandas/issues/137 - "ignore:datetime.datetime.utcfromtimestamp():DeprecationWarning:dateutil.tz.tz", # https://github.com/dateutil/dateutil/pull/1285 + "ignore:\\s*Pyarrow will become a required dependency of pandas:DeprecationWarning", # Will go away by itself in Pandas 3.0 + "ignore:Passing a (SingleBlockManager|BlockManager) to (Series|GeoSeries|DataFrame|GeoDataFrame) is deprecated:DeprecationWarning", # https://github.com/holoviz/spatialpandas/issues/137 + "ignore:datetime.datetime.utcfromtimestamp():DeprecationWarning:dateutil.tz.tz", # https://github.com/dateutil/dateutil/pull/1285 # 2024-02 - "ignore:The current Dask DataFrame implementation is deprecated:DeprecationWarning", # https://github.com/dask/dask/issues/10917 + "ignore:The current Dask DataFrame implementation is deprecated:DeprecationWarning", # https://github.com/dask/dask/issues/10917 # 2024-03 "ignore:\\s*Dask dataframe query planning is disabled because dask-expr is not installed:FutureWarning", - "ignore:numpy.ndarray size changed, may indicate binary incompatibility:RuntimeWarning", # https://github.com/pydata/xarray/issues/7259 + "ignore:numpy.ndarray size changed, may indicate binary incompatibility:RuntimeWarning", # https://github.com/pydata/xarray/issues/7259 ] diff --git a/scripts/conda/build.sh b/scripts/conda/build.sh index 0844318d..f13b2b0a 100755 --- a/scripts/conda/build.sh +++ b/scripts/conda/build.sh @@ -2,16 +2,25 @@ set -euxo pipefail -git status +PACKAGE="geoviews" -export SETUPTOOLS_ENABLE_FEATURES="legacy-editable" -python -m build -w . +python -m build . # Can add -w when this is solved: https://github.com/pypa/hatch/issues/1305 -git diff --exit-code - -VERSION=$(find dist -name "*.whl" -exec basename {} \; | cut -d- -f2) +VERSION=$(python -c "import $PACKAGE; print($PACKAGE._version.__version__)") export VERSION -# Note: pyct is needed in the same environment as conda-build! -conda build scripts/conda/recipe-core --no-anaconda-upload --no-verify -c pyviz -c bokeh -conda build scripts/conda/recipe-recommended --no-anaconda-upload --no-verify -c pyviz -c bokeh +BK_CHANNEL=$(python -c " +import bokeh +from packaging.version import Version + +if Version(bokeh.__version__).is_devrelease: + print('bokeh/label/dev') +else: + print('bokeh') +") + +conda build scripts/conda/recipe-core --no-anaconda-upload --no-verify -c "$BK_CHANNEL" +conda build scripts/conda/recipe-recommended --no-anaconda-upload --no-verify -c "$BK_CHANNEL" + +mv "$CONDA_PREFIX/conda-bld/noarch/$PACKAGE-core-$VERSION-py_0.tar.bz2" dist +mv "$CONDA_PREFIX/conda-bld/noarch/$PACKAGE-$VERSION-py_0.tar.bz2" dist diff --git a/scripts/conda/recipe-core/meta.yaml b/scripts/conda/recipe-core/meta.yaml index e3382a2f..89395f52 100644 --- a/scripts/conda/recipe-core/meta.yaml +++ b/scripts/conda/recipe-core/meta.yaml @@ -1,37 +1,44 @@ -{% set sdata = load_setup_py_data(setup_file="../../../setup.py", from_recipe_dir=True) %} +{% set pyproject = load_file_data('../../../pyproject.toml', from_recipe_dir=True) %} +{% set project = pyproject['project'] %} package: - name: {{ sdata['name'] }}-core + name: {{ project["name"] }}-core version: {{ VERSION }} source: - url: ../../../dist/{{ sdata['name'] }}-{{ VERSION }}-py3-none-any.whl + url: ../../../dist/{{ project["name"] }}-{{ VERSION }}-py3-none-any.whl build: noarch: python - script: {{ PYTHON }} -m pip install -vv {{ sdata['name'] }}-{{ VERSION }}-py3-none-any.whl + script: {{ PYTHON }} -m pip install --no-deps -vv {{ project["name"] }}-{{ VERSION }}-py3-none-any.whl entry_points: - {% for group,epoints in sdata.get("entry_points",{}).items() %} + {% for group,epoints in project.get("entry_points",{}).items() %} {% for entry_point in epoints %} - {{ entry_point }} {% endfor %} {% endfor %} requirements: - run_constrained: - - geoviews {{ sdata['version'] }} - host: - - python {{ sdata['python_requires'] }} - {% for dep in sdata['extras_require']['build'] %} + build: + - python {{ project['requires-python'] }} + {% for dep in pyproject['build-system']['requires'] %} - {{ dep }} {% endfor %} run: - - python {{ sdata['python_requires'] }} - {% for dep in sdata.get('install_requires',{}) %} + - python {{ project['requires-python'] }} + {% for dep in project.get('dependencies', []) %} - {{ dep }} {% endfor %} +test: + imports: + - {{ project["name"] }} + commands: + - pip check + requires: + - pip + about: - home: {{ sdata['url'] }} - summary: {{ sdata['description'] }} - license: {{ sdata['license'] }} + home: {{ project['urls']['Homepage'] }} + summary: {{ project['description'] }} + license: {{ project['license']['text'] }} diff --git a/scripts/conda/recipe-recommended/meta.yaml b/scripts/conda/recipe-recommended/meta.yaml index 4f08f0b0..b7823cab 100644 --- a/scripts/conda/recipe-recommended/meta.yaml +++ b/scripts/conda/recipe-recommended/meta.yaml @@ -1,29 +1,30 @@ -{% set sdata = load_setup_py_data(setup_file="../../../setup.py", from_recipe_dir=True) %} +{% set pyproject = load_file_data('../../../pyproject.toml', from_recipe_dir=True) %} +{% set project = pyproject['project'] %} package: - name: {{ sdata['name'] }} + name: {{ project["name"] }} version: {{ VERSION }} source: - url: ../../../dist/{{ sdata['name'] }}-{{ VERSION }}-py3-none-any.whl + url: ../../../dist/{{ project["name"] }}-{{ VERSION }}-py3-none-any.whl build: noarch: python requirements: host: - - python {{ sdata['python_requires'] }} - {% for dep in sdata['extras_require']['build'] %} + - python {{ project['requires-python'] }} + {% for dep in pyproject['build-system']['requires'] %} - {{ dep }} {% endfor %} run: - - python {{ sdata['python_requires'] }} - - geoviews-core =={{ sdata['version'] }} - {% for dep in sdata['extras_require']['recommended'] %} - - {{ dep if dep != 'geopandas' else 'geopandas-base'}} + - python {{ project['requires-python'] }} + - geoviews-core =={{ VERSION }} + {% for dep in project['optional-dependencies']['recommended'] %} + - {{ dep.replace("geopandas", "geopandas-base").replace("matplotlib", "matplotlib-base") }} {% endfor %} about: - home: {{ sdata['url'] }} - summary: {{ sdata['description'] }} - license: {{ sdata['license'] }} + home: {{ project['urls']['Homepage'] }} + summary: {{ project['description'] }} + license: {{ project['license']['text'] }} diff --git a/scripts/sync_git_tags.py b/scripts/sync_git_tags.py new file mode 100644 index 00000000..a34e9808 --- /dev/null +++ b/scripts/sync_git_tags.py @@ -0,0 +1,35 @@ +""" +Script to sync tags from upstream repository to forked repository +""" + +import sys +from subprocess import run + + +def main(package: str) -> None: + origin = run( + ["git", "remote", "get-url", "origin"], check=True, capture_output=True + ) + upstream = run( + ["git", "remote", "get-url", "upstream"], check=False, capture_output=True + ) + url = ( + f"https://github.com/holoviz/{package}.git" + if origin.stdout.startswith(b"http") + else f"git@github.com:holoviz/{package}.git" + ) + + if url == origin.stdout.strip().decode(): + print("Not a forked repository, exiting.") + return + elif upstream.returncode: + print(f"Adding {url!r} as remote upstream") + run(["git", "remote", "add", "upstream", url], check=True, capture_output=True) + + print(f"Syncing tags from {package} repository with your forked repository") + run(["git", "fetch", "--tags", "upstream"], check=True, capture_output=True) + run(["git", "push", "--tags"], check=True, capture_output=True) + + +if __name__ == "__main__": + main(sys.argv[1]) diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 5a777ab9..00000000 --- a/setup.cfg +++ /dev/null @@ -1,7 +0,0 @@ -[metadata] -license_files = LICENSE - -[tool:pyctdev.conda] -namespace_map = - geopandas=geopandas-base - matplotlib=matplotlib-base diff --git a/setup.py b/setup.py deleted file mode 100644 index ece8e191..00000000 --- a/setup.py +++ /dev/null @@ -1,231 +0,0 @@ -#!/usr/bin/env python - -import sys,os,json -import shutil - -from setuptools import setup, find_packages -from setuptools.command.develop import develop -from setuptools.command.install import install -from setuptools.command.sdist import sdist - -import pyct.build - -############### -### autover ### - - -def get_setup_version(reponame): - """ - Helper to get the current version from either git describe or the - .version file (if available). - """ - basepath = os.path.split(__file__)[0] - version_file_path = os.path.join(basepath, reponame, '.version') - try: - from param import version - except: - version = None - if version is not None: - return version.Version.setup_version(basepath, reponame, archive_commit="$Format:%h$") - else: - print("WARNING: param>=1.6.0 unavailable. If you are installing a package, this warning can safely be ignored. If you are creating a package or otherwise operating in a git repository, you should install param>=1.6.0.") - return json.load(open(version_file_path, 'r'))['version_string'] - - -####################### -### bokeh extension ### - - -def _build_geoviewsjs(): - from bokeh.ext import build - print("Building custom models:") - geoviews_dir = os.path.join(os.path.dirname(__file__), "geoviews") - build(geoviews_dir) - - -class CustomDevelopCommand(develop): - """Custom installation for development mode.""" - - def run(self): - _build_geoviewsjs() - develop.run(self) - - -class CustomInstallCommand(install): - """Custom installation for install mode.""" - - def run(self): - _build_geoviewsjs() - install.run(self) - - -class CustomSdistCommand(sdist): - """Custom installation for sdist mode.""" - - def run(self): - _build_geoviewsjs() - sdist.run(self) - - -_COMMANDS = { - 'develop': CustomDevelopCommand, - 'install': CustomInstallCommand, - 'sdist': CustomSdistCommand, -} - -try: - from wheel.bdist_wheel import bdist_wheel - - class CustomBdistWheelCommand(bdist_wheel): - """Custom bdist_wheel command to force cancelling qiskit-terra wheel - creation.""" - - def run(self): - """Do nothing so the command intentionally fails.""" - _build_geoviewsjs() - bdist_wheel.run(self) - - _COMMANDS['bdist_wheel'] = CustomBdistWheelCommand -except: - pass - - -#################### -### dependencies ### - -_required = [ - 'bokeh >=3.4.0,<3.5.0', - 'cartopy >=0.18.0', - 'holoviews >=1.16.0', - 'packaging', - 'numpy', - 'shapely', - 'param', - 'panel >=1.0.0', - 'pyproj', - 'xyzservices', -] - -_recommended = [ - # geopandas-base installed with conda, see setup.cfg - 'geopandas', - 'netcdf4', - 'matplotlib >2.2', - 'pandas', - 'pyct', - 'scipy', - 'shapely', - 'xarray', - 'datashader', - 'pooch', -] - -# can only currently run all examples with packages from conda-forge -_examples_extra = _recommended + [ - 'iris >=3.5', # Pin to support numpy 1.24 - 'xesmf', - 'mock', - 'fiona', - 'geodatasets', -] -extras_require={ - 'recommended': _recommended, - 'examples_extra': _examples_extra, - 'doc': _examples_extra + [ - 'nbsite >=0.8.4,<0.9.0', - 'cartopy >=0.20.0', - 'graphviz', - 'lxml', - 'selenium', - 'pooch', - ], - 'tests_core': [ - # Combination of tests and recommended without numba - 'pytest', - 'geopandas', - 'netcdf4', - 'matplotlib >2.2', - 'pandas', - 'scipy', - 'shapely', - 'xarray', - 'pooch', - 'pyviz_comms', - ], - 'tests': [ - 'nbval', - 'pytest', - 'fiona', - 'rioxarray', - ], - 'tests_ci': [ - 'pytest-cov', - 'codecov', - 'pytest-github-actions-annotate-failures', - ] -} - -extras_require['all'] = sorted(set(sum(extras_require.values(), []))) - -# until pyproject.toml/equivalent is widely supported; meanwhile -# setup_requires doesn't work well with pip. Note: deliberately omitted from all. -extras_require['build'] = [ - 'param >=1.9.2', - 'pyct >=0.4.4', - 'bokeh ==3.4', - 'setuptools', -] - -######################## -### package metadata ### - - -setup_args = dict( - name='geoviews', - version=get_setup_version("geoviews"), - python_requires = '>=3.9', - install_requires = _required, - extras_require = extras_require, - tests_require = extras_require['tests'], - description='GeoViews is a Python library that makes it easy to explore and visualize geographical, meteorological, and oceanographic datasets, such as those used in weather, climate, and remote sensing research.', - long_description=open("README.md").read(), - long_description_content_type="text/markdown", - platforms=['Windows', 'Mac OS X', 'Linux'], - license='BSD 3-Clause', - url='https://geoviews.org', - cmdclass=_COMMANDS, - packages =find_packages(), - include_package_data=True, - entry_points={ - 'console_scripts': [ - 'geoviews = geoviews.__main__:main' - ] - }, - classifiers = [ - "License :: OSI Approved :: BSD License", - "Development Status :: 5 - Production/Stable", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Operating System :: OS Independent", - "Intended Audience :: Science/Research", - "Intended Audience :: Developers", - "Natural Language :: English", - "Framework :: Matplotlib", - "Topic :: Scientific/Engineering", - "Topic :: Software Development :: Libraries" - ] -) - -if __name__=="__main__": - example_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), - 'geoviews','examples') - - if 'develop' not in sys.argv and 'egg_info' not in sys.argv: - pyct.build.examples(example_path, __file__, force=True) - - setup(**setup_args) - - if os.path.isdir(example_path): - shutil.rmtree(example_path) diff --git a/tox.ini b/tox.ini deleted file mode 100644 index c1951952..00000000 --- a/tox.ini +++ /dev/null @@ -1,55 +0,0 @@ -# For use with pyct (https://github.com/holoviz-dev/pyctdev), but just standard -# tox config (works with tox alone). - -[tox] -# python version test group extra envs extra commands -envlist = {py39,py310,py311,py312}-{unit,examples,all_recommended,examples_extra,simple}-{default}-{dev,pkg} - -[_simple] -description = Install geoviews without any optional dependencies -deps = . -commands = python -c "import geoviews as gv; print(gv.__version__)" - - -[_unit] -description = Run unit tests with coverage -deps = .[tests] -commands = pytest geoviews --cov=./geoviews - -[_examples] -description = Test that default examples run -deps = .[recommended, tests] -commands = pytest --nbval-lax examples - -[_examples_extra] -description = Test that all examples run -deps = .[examples_extra, tests] -commands = pytest --nbval-lax examples - -[_all_recommended] -description = Run all recommended tests -deps = .[recommended, tests] -commands = {[_unit]commands} - {[_examples]commands} - -[_pkg] -commands = geoviews examples --path=. --force - bokeh sampledata - -[testenv] -sitepackages = True -install_command = pip install --no-deps {opts} {packages} - -changedir = {envtmpdir} - -commands = examples-pkg: {[_pkg]commands} - unit: {[_unit]commands} - examples: {[_examples]commands} - examples_extra: {[_examples_extra]commands} - simple: {[_simple]commands} - all_recommended: {[_all_recommended]commands} - -deps = unit: {[_unit]deps} - examples: {[_examples]deps} - examples_extra: {[_examples_extra]deps} - all_recommended: {[_all_recommended]deps}