diff --git a/.github/ISSUE_TEMPLATE/release.md b/.github/ISSUE_TEMPLATE/release.md index 8c533e99..9bdb55c1 100644 --- a/.github/ISSUE_TEMPLATE/release.md +++ b/.github/ISSUE_TEMPLATE/release.md @@ -5,13 +5,14 @@ - [ ] `git clean -dxf && mkdir dist` - [ ] Find the [workflow run](https://github.com/openslide/openslide-python/actions) for the tag; download its docs and wheels artifacts - [ ] `unzip /path/to/downloaded/openslide-python-wheels.zip && mv openslide-python-wheels-*/* dist/` -- [ ] `python setup.py sdist` - [ ] `twine upload dist/*` - [ ] Recompress tarball with `xz` - [ ] Attach release notes to [GitHub release](https://github.com/openslide/openslide-python/releases/new); upload tarballs and wheels - [ ] `cd` into website checkout; `rm -r api/python && unzip /path/to/downloaded/openslide-python-docs.zip && mv openslide-python-docs-* api/python` - [ ] Update website: `_data/releases.yaml`, `_includes/news.md` - [ ] Update Ubuntu PPA -- [ ] Send mail to -announce and -users - [ ] Update Fedora and EPEL packages +- [ ] Check that [Copr package](https://copr.fedorainfracloud.org/coprs/g/openslide/openslide/builds/) built successfully +- [ ] Send mail to -announce and -users +- [ ] Post to [forum.image.sc](https://forum.image.sc/c/announcements/10) - [ ] Update MacPorts package diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index d5d8287a..296f2a0d 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -31,17 +31,48 @@ jobs: matrix: os: [ubuntu-latest, macos-latest] python-version: [3.8, 3.9, "3.10", "3.11", "3.12"] + include: + - os: ubuntu-latest + python-version: "3.12" + sdist: sdist + # Python 3.8 is too old to support universal binaries, and + # setup-python's Python 3.9 and 3.10 won't build them. Use the + # last upstream patch releases that ship with installers. + # https://github.com/actions/setup-python/issues/439#issuecomment-1247646682 + - os: macos-latest + python-version: "3.9" + upstream-python: 3.9.13 + - os: macos-latest + python-version: "3.10" + upstream-python: 3.10.11 steps: - name: Check out repo uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} + if: matrix.upstream-python == null uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} + - name: Set up Python ${{ matrix.python-version }} (macOS fallback) + if: matrix.upstream-python != null + run: | + pkgdir="${{ runner.temp }}/python" + mkdir -p "$pkgdir/bin" + pkg="$pkgdir/python.pkg" + curl -Lfo "$pkg" \ + "https://www.python.org/ftp/python/${{ matrix.upstream-python }}/python-${{ matrix.upstream-python }}-macos11.pkg" + sudo installer -pkg "$pkg" -target / + for bin in python pip; do + ln -s /usr/local/bin/${bin}3 $pkgdir/bin/${bin} + done + export PATH="$pkgdir/bin:$PATH" + echo "PATH=$PATH" >> $GITHUB_ENV + python -V + pip -V - name: Install Python tools run: | python -m pip install --upgrade pip - pip install jinja2 pytest setuptools + pip install auditwheel build jinja2 pytest setuptools - name: Install OpenSlide run: | case "${{ matrix.os }}" in @@ -52,12 +83,40 @@ jobs: brew install openslide ;; esac + - name: Build wheel + run: | + if [ -z "${{ matrix.sdist }}" ]; then + wheel_only=-w + fi + python -m build $wheel_only + case "${{ matrix.os }}" in + ubuntu-*) + mkdir old + mv dist/*.whl old/ + auditwheel repair --only-plat -w dist old/*whl + ;; + macos-*) + if [ "${{ matrix.python-version }}" != 3.8 -a ! -e dist/*universal2* ]; then + echo "Wheel is not universal:" + ls dist + exit 1 + fi + esac + basename=openslide-python-wheels-$GITHUB_RUN_NUMBER-$(echo $GITHUB_SHA | cut -c-10) + mkdir -p "artifacts/${basename}" + mv dist/* "artifacts/${basename}" + echo "basename=${basename}" >> $GITHUB_ENV - name: Install - run: pip install . + run: pip install artifacts/${basename}/*.whl - name: Run tests run: pytest -v - name: Tile slide run: python examples/deepzoom/deepzoom_tile.py --viewer -o tiled tests/fixtures/small.svs + - name: Archive wheel + uses: actions/upload-artifact@v3 + with: + name: ${{ env.basename }} + path: artifacts windows: name: Windows needs: pre-commit @@ -80,24 +139,24 @@ jobs: - name: Install Python tools run: | python -m pip install --upgrade pip - pip install flask Pillow pytest setuptools wheel + pip install build flask pytest - name: Install OpenSlide run: | mkdir -p c:\\openslide cd c:\\openslide zipname=openslide-win64-${BIN_RELEASE} - curl -LO "https://github.com/openslide/openslide-bin/releases/download/v${BIN_RELEASE}/${zipname}.zip" + curl -LfO "https://github.com/openslide/openslide-bin/releases/download/v${BIN_RELEASE}/${zipname}.zip" 7z x ${zipname}.zip echo "OPENSLIDE_PATH=c:\\openslide\\${zipname}\\bin" >> $GITHUB_ENV - name: Build wheel run: | - python setup.py bdist_wheel + python -m build -w basename=openslide-python-wheels-$GITHUB_RUN_NUMBER-$(echo $GITHUB_SHA | cut -c-10) mkdir -p "artifacts/${basename}" mv dist/*.whl "artifacts/${basename}" echo "basename=${basename}" >> $GITHUB_ENV - name: Install - run: pip install -e . + run: pip install artifacts/${basename}/*.whl - name: Run tests # Reads OPENSLIDE_PATH run: pytest -v diff --git a/.gitignore b/.gitignore index 87d0e176..d3dc441a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ /build /dist -/MANIFEST /*.egg-info *.pyc diff --git a/.isort.cfg b/.isort.cfg deleted file mode 100644 index 716bf5fc..00000000 --- a/.isort.cfg +++ /dev/null @@ -1,3 +0,0 @@ -[settings] -profile = black -force_sort_within_sections = true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a1043487..6ecd122d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,7 @@ exclude: '^(COPYING\.LESSER|examples/deepzoom/static/.*\.js)$' repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-added-large-files - id: check-merge-conflict @@ -14,7 +14,7 @@ repos: exclude: '^\.github/.*\.md$' - repo: https://github.com/asottile/pyupgrade - rev: v3.14.0 + rev: v3.15.0 hooks: - id: pyupgrade name: Modernize python code @@ -27,7 +27,7 @@ repos: name: Reorder python imports with isort - repo: https://github.com/psf/black - rev: 23.9.1 + rev: 23.10.1 hooks: - id: black name: Format python code with black @@ -57,7 +57,7 @@ repos: hooks: - id: rstcheck name: Validate reStructuredText syntax - additional_dependencies: [sphinx] + additional_dependencies: [sphinx, toml] - repo: meta hooks: diff --git a/.rstcheck.cfg b/.rstcheck.cfg deleted file mode 100644 index 38ae5093..00000000 --- a/.rstcheck.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[rstcheck] -ignore_messages=(Hyperlink target ".*" is not referenced\.$) diff --git a/MANIFEST.in b/MANIFEST.in index b84f5159..2aa1e1d6 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,4 @@ -include *.md pytest.ini +include *.md recursive-include doc *.py *.rst recursive-include examples *.html *.js *.png *.py recursive-include tests *.dcm *.png *.py *.svs *.tiff diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..53914a58 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,60 @@ +[project] +name = "openslide-python" +maintainers = [ + {name = "OpenSlide project", email = "openslide-users@lists.andrew.cmu.edu"} +] +description = "Python interface to OpenSlide" +readme = "README.md" +license = {text = "GNU Lesser General Public License, version 2.1"} +keywords = ["OpenSlide", "whole-slide image", "virtual slide", "library"] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: Healthcare Industry", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: GNU Lesser General Public License v2 (LGPLv2)", + "Operating System :: MacOS :: MacOS X", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Scientific/Engineering :: Bio-Informatics", +] +requires-python = ">= 3.8" +dependencies = ["Pillow"] +dynamic = ["version"] + +[project.urls] +Homepage = "https://openslide.org/" +Documentation = "https://openslide.org/api/python/" +Repository = "https://github.com/openslide/openslide-python" + +[tool.setuptools] +packages = ["openslide"] + +[tool.setuptools.dynamic] +version = {attr = "openslide._version.__version__"} + +[tool.isort] +profile = "black" +force_sort_within_sections = true + +[tool.pytest.ini_options] +minversion = "7.0" +# don't try to import openslide from the source directory, since it doesn't +# have the compiled extension module +addopts = "--import-mode importlib" +# allow tests to import common module +pythonpath = "tests" + +[tool.rstcheck] +ignore_messages = "(Hyperlink target \".*\" is not referenced\\.$)" + +[build-system] +requires = ["setuptools >= 61.0.0"] +build-backend = "setuptools.build_meta" diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index 8581b237..00000000 --- a/pytest.ini +++ /dev/null @@ -1,7 +0,0 @@ -[pytest] -minversion = 7.0 -# don't try to import openslide from the source directory, since it doesn't -# have the compiled extension module -addopts = --import-mode importlib -# allow tests to import common module -pythonpath = tests diff --git a/setup.py b/setup.py index 34a71afc..8f821a95 100644 --- a/setup.py +++ b/setup.py @@ -1,54 +1,7 @@ -import os - from setuptools import Extension, setup -# Load version string -_verfile = os.path.join(os.path.dirname(__file__), 'openslide', '_version.py') -with open(_verfile) as _fh: - exec(_fh.read()) # instantiates __version__ - -with open('README.md') as _fh: - _long_description = _fh.read() - setup( - name='openslide-python', - version=__version__, # noqa: F821 undefined-name __version__ - packages=[ - 'openslide', - ], ext_modules=[ Extension('openslide._convert', ['openslide/_convert.c']), ], - test_suite='tests', - maintainer='OpenSlide project', - maintainer_email='openslide-users@lists.andrew.cmu.edu', - description='Python interface to OpenSlide', - long_description=_long_description, - long_description_content_type='text/markdown', - license='GNU Lesser General Public License, version 2.1', - keywords='openslide whole-slide image virtual slide library', - url='https://openslide.org/', - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'Intended Audience :: Healthcare Industry', - 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: GNU Lesser General Public License v2 (LGPLv2)', - 'Operating System :: MacOS :: MacOS X', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: POSIX :: Linux', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.12', - 'Topic :: Scientific/Engineering :: Bio-Informatics', - ], - python_requires='>=3.8', - install_requires=[ - 'Pillow', - ], - zip_safe=True, )