diff --git a/.github/workflows/package_and_test.yml b/.github/workflows/package_and_test.yml new file mode 100644 index 0000000..4e1a195 --- /dev/null +++ b/.github/workflows/package_and_test.yml @@ -0,0 +1,9 @@ +name: Package & Test + +on: [push, pull_request] + +jobs: + package_and_test: + name: Package and Test + # Use the "reusable workflow" from the hyperspy organisation + uses: hyperspy/.github/.github/workflows/package_and_test.yml@main diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index eaa8914..1f85c1e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,9 +1,12 @@ name: Release +# Reusable workflow are not supported with trusted publisher +# https://github.com/pypa/gh-action-pypi-publish/issues/166 +# copy and paste most of +# https://github.com/hyperspy/.github/blob/main/.github/workflows/release_pure_python.yml + # This workflow builds the wheels "on tag". -# If run from the hyperspy/hyperspy_gui_ipywidgets repository, the wheels will be -# uploaded to pypi; otherwise, the wheels will be available as a github artifact. -# This workflow overwrite the version in `hyperspy_gui_ipywidgets/version.py` -# with the tag +# If run from the hyperspy/hyperspy repository, the wheels will be uploaded to pypi ; +# otherwise, the wheels will be available as a github artifact. on: push: # Sequence of patterns matched against refs/tags @@ -11,86 +14,42 @@ on: - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 jobs: - create_release: + package_and_test: + name: Package and Test + # Use the "reusable workflow" from the hyperspy organisation + uses: hyperspy/.github/.github/workflows/package_and_test.yml@main + + upload_to_pypi: + needs: [package_and_test] + runs-on: ubuntu-latest + name: Upload to pypi + permissions: + # IMPORTANT: this permission is mandatory for trusted publishing + id-token: write + steps: + - name: Download dist + uses: actions/download-artifact@v3 + + - name: Display downloaded files + run: | + ls -shR + working-directory: dist + + - uses: pypa/gh-action-pypi-publish@release/v1 + if: ${{ startsWith(github.ref, 'refs/tags/') && github.repository_owner == 'hyperspy' }} + # See https://docs.pypi.org/trusted-publishers/using-a-publisher/ + + create_github_release: + # If zenodo is setup to create a DOI automatically on a GitHub release, + # this step will trigger the mining of the DOI + needs: upload_to_pypi permissions: contents: write - name: Create Release + name: Create GitHub Release runs-on: ubuntu-latest - outputs: - upload_url: ${{ steps.create_release.outputs.upload_url }} - VERSION: ${{ env.VERSION }} steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions - with: - tag_name: ${{ github.ref }} - release_name: Release ${{ github.ref }} - draft: false - prerelease: false - - name: Get version (on tag) - if: startsWith(github.ref, 'refs/tags/') - run: | - echo "VERSION=${GITHUB_REF#refs/*/v}" >> $GITHUB_ENV - - build_wheels_linux: - name: Wheels on ubuntu-latest - needs: create_release - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: 3.8 - - - name: Install release dependencies - run: | - python -m pip install twine wheel - - - name: Overwrite `version.py` with tag - if: startsWith(github.ref, 'refs/tags/') - env: - version_file: hyperspy_gui_ipywidgets/version.py - VERSION: ${{ needs.create_release.outputs.VERSION }} - run: | - echo '__version__ = "${{ env.VERSION }}"' > ${{ env.version_file }} - - - name: Build source distribution - run: | - python setup.py sdist bdist_wheel - - - name: Display content dist folder - run: | - ls dist/ - - - name: Install and test distribution - env: - MPLBACKEND: agg - run: | - pip install --find-links dist hyperspy_gui_ipywidgets[tests] - pytest --pyargs hyperspy_gui_ipywidgets - - - uses: actions/upload-artifact@v3 - with: - path: | - ./dist/*.whl - ./dist/*.tar.gz - - - name: Publish wheels to PyPI - if: github.repository_owner == 'hyperspy' - env: - # Github secret set in the hyperspy/hyperspy_gui_ipywidgets repository - # Not available from fork or pull request - # Secrets are not passed to workflows that are triggered by a pull request from a fork - TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} - TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} - run: | - twine upload dist/*.whl --verbose - twine upload dist/*.tar.gz --verbose - + if: ${{ startsWith(github.ref, 'refs/tags/') && github.repository_owner == 'hyperspy' }} + uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 47230a7..d9eca67 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,27 +9,35 @@ jobs: timeout-minutes: 30 env: MPLBACKEND: agg - PIP_SELECTOR: '[tests, coverage]' + PIP_SELECTOR: '[tests]' PYTEST_ARGS: --pyargs hyperspy_gui_ipywidgets PYTEST_ARGS_COVERAGE: --cov=. --cov-report=xml strategy: fail-fast: false matrix: PYTHON_VERSION: ['3.9', '3.10', '3.11'] - LABEL: [-RnM] - PIP_ARGS: [--upgrade] + LABEL: [-release, -RnM] + PIP_ARGS: [""] include: - PYTHON_VERSION: '3.8' - PIP_ARGS: --pre --upgrade + PIP_ARGS: --pre LABEL: -pre_release-RnM - - PYTHON_VERSION: '3.8' - PIP_ARGS: --pre --upgrade - LABEL: -mininum-RnM - # - PYTHON_VERSION: '3.8' - # LABEL: -RnP + - PYTHON_VERSION: '3.12' + LABEL: -minimum + - PYTHON_VERSION: '3.12' + LABEL: -RnM steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + + - name: Fetch tags upstream + if: ${{ github.repository_owner != 'hyperspy' }} + # Needs to fetch the tags from upstream to get the + # correct version with setuptools_scm + run: | + git remote add upstream https://github.com/hyperspy/hyperspy_gui_ipywidgets.git + git fetch --prune --unshallow + git fetch upstream --tags - uses: actions/setup-python@v4 name: Install Python @@ -42,25 +50,21 @@ jobs: pip --version - name: Install HyperSpy (RELEASE_next_major) - shell: bash if: contains( matrix.LABEL, 'RnM') run: | - pip install https://github.com/hyperspy/hyperspy/archive/RELEASE_next_major.zip + pip install git+https://github.com/hyperspy/hyperspy.git@RELEASE_next_major - name: Install HyperSpy (RELEASE_next_patch) - shell: bash if: contains( matrix.LABEL, 'RnP') run: | - pip install https://github.com/hyperspy/hyperspy/archive/RELEASE_next_patch.zip + pip install git+https://github.com/hyperspy/hyperspy.git@RELEASE_next_patch - name: Install exSpy - if: "!contains( matrix.LABEL, 'mininum')" - shell: bash + if: ${{ ! contains( matrix.LABEL, 'minimum') }} run: | - pip install https://github.com/hyperspy/exspy/archive/main.zip + pip install git+https://github.com/hyperspy/exspy.git - name: Install - shell: bash run: | pip install ${{ matrix.PIP_ARGS }} .'${{ env.PIP_SELECTOR }}' diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 0d7237b..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,3 +0,0 @@ -include README.md -include LICENSE - diff --git a/hyperspy_gui_ipywidgets/__init__.py b/hyperspy_gui_ipywidgets/__init__.py index b0a9d4b..b0ccaf4 100644 --- a/hyperspy_gui_ipywidgets/__init__.py +++ b/hyperspy_gui_ipywidgets/__init__.py @@ -18,6 +18,19 @@ import importlib +from pathlib import Path + + +if Path(__file__).parent.parent.name == "site-packages": # pragma: no cover + # Tested in the "Package & Test" workflow on GitHub CI + from importlib.metadata import version + + __version__ = version("hyperspy_gui_ipywidgets") +else: + # Editable install + from setuptools_scm import get_version + + __version__ = get_version(Path(__file__).parent.parent) __all__ = [ @@ -31,21 +44,14 @@ ] -# mapping following the pattern: from value import key -_import_mapping = { - '__version__':'.version', - } - - def __dir__(): return sorted(__all__) def __getattr__(name): if name in __all__: - if name in _import_mapping.keys(): - import_path = 'hyperspy_gui_ipywidgets' + _import_mapping.get(name) - return getattr(importlib.import_module(import_path), name) + if name == "__version__": + return __version__ else: return importlib.import_module( "." + name, 'hyperspy_gui_ipywidgets' diff --git a/hyperspy_gui_ipywidgets/version.py b/hyperspy_gui_ipywidgets/version.py deleted file mode 100644 index b3ba3e2..0000000 --- a/hyperspy_gui_ipywidgets/version.py +++ /dev/null @@ -1,2 +0,0 @@ - -__version__ = "2.0.dev0" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..ee0e200 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,93 @@ +[build-system] +requires = ["setuptools>=64", "setuptools_scm>=8", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "hyperspy_gui_ipywidgets" +description = "ipywidgets GUI elements for the HyperSpy framework." +requires-python = ">=3.8" +readme = "README.md" +keywords=[ + "data analysis", + "microscopy", + "ipywidgets", + "hyperspy", + "multi-dimensional", +] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "Topic :: Software Development :: Libraries", + "Topic :: Scientific/Engineering", + "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX", + "Operating System :: Unix", + "Operating System :: MacOS", +] +dependencies = [ + "hyperspy>=2.0rc0", + "ipywidgets>=7.0", + "link_traits", +] +dynamic = ["version"] + +[project.entry-points."hyperspy.extensions"] +hyperspy-gui-ipywidgets = "hyperspy_gui_ipywidgets" + +[project.license] +file = "LICENSE" + +[project.optional-dependencies] +tests = [ + "pytest", + "pytest-cov", + "pytest-rerunfailures", + "setuptools-scm", +] +dev = [ + "black", + "hyperspy-gui-ipywidgets[doc]", + "hyperspy-gui-ipywidgets[tests]" +] + +[project.urls] +"Homepage" = "https://github.com/hyperspy/hyperspy_gui_ipywidgets" +"Bug Reports" = "https://github.com/hyperspy/hyperspy_gui_ipywidgets/issues" +"Source" = "https://github.com/hyperspy/hyperspy_gui_ipywidgets" +"Conda-Forge" = "https://anaconda.org/conda-forge/hyperspy-gui-ipywidgets" +"Support" = "https://gitter.im/hyperspy/hyperspy" + +[tool.coverage.run] +branch = true +source = ["hyperspy_gui_ipywidgets"] +omit = [ + "hyperspy_gui_ipywidgets/tests/*", + "hyperspy_gui_ipywidgets/conftest.py", +] + +[tool.coverage.report] +precision = 2 + +[tool.pytest.ini_options] +# "-ra", # Display summary: "all except passes" +addopts = "-ra" +minversion = "6.0" +testpaths = [ + "hyperspy_gui_ipywidgets/tests", +] + +[tool.setuptools.packages.find] +include = ["hyperspy_gui_ipywidgets*"] + +[tool.setuptools.package-data] +"*" = ["*.yaml"] + +[tool.setuptools_scm] +# Presence enables setuptools_scm, the version will be determine at build time from git diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 3b5dd1d..0000000 --- a/setup.cfg +++ /dev/null @@ -1,8 +0,0 @@ -[coverage:run] -branch = True -source = hyperspy_gui_ipywidgets -include = */hyperspy_gui_ipywidgets/* -omit = - */hyperspy_gui_ipywidgets/tests/* - */hyperspy/conftest.py - */setup.py diff --git a/setup.py b/setup.py deleted file mode 100644 index 5a63c98..0000000 --- a/setup.py +++ /dev/null @@ -1,101 +0,0 @@ -"""ipywidgets GUI elements for HyperSpy. - -""" -# setup.py adapted from https://github.com/pypa/sampleproject/blob/master/setup.py -# Always prefer setuptools over distutils -from setuptools import setup, find_packages -# To use a consistent encoding -from codecs import open -from os import path - -here = path.abspath(path.dirname(__file__)) - -# Get the long description from the README file -with open(path.join(here, 'README.md'), encoding='utf-8') as f: - long_description = f.read() - - -version = {} -with open(path.join(here, "hyperspy_gui_ipywidgets", "version.py")) as fp: - exec(fp.read(), version) - -PROJECT_URLS = { - 'Bug Tracker': 'https://github.com/hyperspy/hyperspy_gui_ipywidgets/issues', - 'Changelog' : 'https://github.com/hyperspy/hyperspy_gui_ipywidgets/blob/main/CHANGES.md', - 'Conda-Forge' : 'https://anaconda.org/conda-forge/hyperspy-gui-ipywidgets', - 'Documentation': 'https://hyperspy.org/hyperspy-doc/current/index.html', - 'Source Code': 'https://github.com/hyperspy/hyperspy_gui_ipywidgets', - 'Support' : 'https://gitter.im/hyperspy/hyperspy' -} - - -setup( - name='hyperspy_gui_ipywidgets', - - # Versions should comply with PEP440. For a discussion on single-sourcing - # the version across setup.py and the project code, see - # https://packaging.python.org/en/latest/single_source_version.html - version=version['__version__'], - - description=('ipywidgets GUI elements for HyperSpy.'), - long_description=long_description, - long_description_content_type="text/markdown", - - # The project's main homepage. - url='https://github.com/hyperspy/hyperspy_gui_ipywidgets', - project_urls=PROJECT_URLS, - - # Author details - author='The HyperSpy Developers', - - # Choose your license - license='GPLv3', - - # See https://pypi.python.org/pypi?%3Aaction=list_classifiers - classifiers=[ - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Development Status :: 4 - Beta", - "Environment :: Console", - "Intended Audience :: Science/Research", - "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", - "Natural Language :: English", - "Operating System :: OS Independent", - "Topic :: Scientific/Engineering", - "Topic :: Scientific/Engineering :: Physics", - ], - - # What does your project relate to? - keywords='hyperspy ipywidgets', - - # You can just specify the packages manually here if your project is - # simple. Or you can use find_packages(). - packages=find_packages(), - - # Alternatively, if you want to distribute just a my_module.py, uncomment - # this: - # py_modules=["my_module"], - - # List run-time dependencies here. These will be installed by pip when - # your project is installed. For an analysis of "install_requires" vs pip's - # requirements files see: - # https://packaging.python.org/en/latest/requirements.html - python_requires='~=3.7', - install_requires=['hyperspy>=2.0rc0', 'ipywidgets>=7.0', 'link_traits'], - - - # List additional groups of dependencies here (e.g. development - # dependencies). You can install these using the following syntax, - # for example: - # $ pip install -e .[dev,test] - extras_require={ - 'tests': ['pytest', 'pytest-rerunfailures'], - 'coverage':["pytest-cov", "codecov"]}, - entry_points={'hyperspy.extensions': 'hyperspy-gui-ipywidgets = hyperspy_gui_ipywidgets'}, - package_data={ # Optional - 'hyperspy_gui_ipywidgets': ['hyperspy_extension.yaml'], -}, -)