Skip to content

Commit

Permalink
Support openslide-bin
Browse files Browse the repository at this point in the history
Prefer loading OpenSlide from the openslide-bin package if available.

Raise a descriptive ModuleNotFoundError on Linux if OpenSlide is not found.

Signed-off-by: Benjamin Gilbert <bgilbert@cs.cmu.edu>
  • Loading branch information
bgilbert committed Sep 7, 2024
1 parent ab1dbfe commit fa9f1be
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 18 deletions.
27 changes: 20 additions & 7 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,11 @@ jobs:
matrix:
os: [ubuntu-latest, macos-latest]
python-version: [3.8, 3.9, "3.10", "3.11", "3.12", "3.13-dev"]
openslide: [system, wheel]
include:
- os: ubuntu-latest
python-version: "3.12"
openslide: system
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
Expand Down Expand Up @@ -86,7 +88,8 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install auditwheel build jinja2 pytest
- name: Install OpenSlide
- name: Install OpenSlide (system)
if: matrix.openslide == 'system'
run: |
case "${{ matrix.os }}" in
ubuntu-latest)
Expand All @@ -99,6 +102,9 @@ jobs:
brew install openslide
;;
esac
- name: Install OpenSlide (wheel)
if: matrix.openslide == 'wheel'
run: pip install openslide-bin
- name: Build dist
run: |
if [ -z "${{ matrix.sdist }}" ]; then
Expand All @@ -124,9 +130,10 @@ jobs:
fi
mkdir -p "artifacts/whl/${{ needs.pre-commit.outputs.dist-base }}"
mv dist/* "artifacts/whl/${{ needs.pre-commit.outputs.dist-base }}"
# save version-specific wheels and oldest abi3 wheel
# from pkg builds, save version-specific wheels and oldest abi3 wheel
python -c 'import sys
if sys.version_info < (3, 12): print("archive_wheel=1")' >> $GITHUB_ENV
if sys.version_info < (3, 12) and "${{ matrix.openslide }}" == "system":
print("archive_wheel=1")' >> $GITHUB_ENV
- name: Install
run: pip install artifacts/whl/${{ needs.pre-commit.outputs.dist-base }}/*.whl
- name: Run tests
Expand Down Expand Up @@ -158,6 +165,7 @@ jobs:
strategy:
matrix:
python-version: [3.8, 3.9, "3.10", "3.11", "3.12", "3.13-dev"]
openslide: [zip, wheel]
steps:
- name: Check out repo
uses: actions/checkout@v4
Expand All @@ -169,7 +177,8 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install build flask pytest
- name: Install OpenSlide
- name: Install OpenSlide (zip)
if: matrix.openslide == 'zip'
env:
GH_TOKEN: ${{ github.token }}
run: |
Expand All @@ -184,14 +193,18 @@ jobs:
--pattern "${zipname}.zip"
7z x ${zipname}.zip
echo "OPENSLIDE_PATH=c:\\openslide\\${zipname}\\bin" >> $GITHUB_ENV
- name: Install OpenSlide (wheel)
if: matrix.openslide == 'wheel'
run: pip install openslide-bin
- name: Build wheel
run: |
python -m build -w
mkdir -p "artifacts/whl/${{ needs.pre-commit.outputs.dist-base }}"
mv dist/*.whl "artifacts/whl/${{ needs.pre-commit.outputs.dist-base }}"
# save version-specific wheels and oldest abi3 wheel
python -c 'import sys
if sys.version_info < (3, 12): print("archive_wheel=1")' >> $GITHUB_ENV
# from zip builds, save version-specific wheels and oldest abi3 wheel
python -c 'import sys;
if sys.version_info < (3, 12) and "${{ matrix.openslide }}" == "zip":
print("archive_wheel=1")' >> $GITHUB_ENV
- name: Install
run: pip install artifacts/whl/${{ needs.pre-commit.outputs.dist-base }}/*.whl
- name: Run tests
Expand Down
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,15 @@ OpenSlide can read virtual slides in several formats:

## Installation

OpenSlide Python requires [OpenSlide]. For instructions on installing both
components so OpenSlide Python can find OpenSlide, see the package
[documentation][installing].
OpenSlide Python requires [OpenSlide]. Install both components from PyPI
with:

```console
pip install openslide-python openslide-bin
```

Or, see the [OpenSlide Python documentation][installing] for instructions on
installing so OpenSlide Python can find OpenSlide.

[installing]: https://openslide.org/api/python/#installing

Expand Down
14 changes: 9 additions & 5 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,20 @@ Installing
==========

OpenSlide Python requires OpenSlide_, which must be installed separately.
If you intend to use OpenSlide only with Python, the easiest way to get it
is to install the openslide-bin_ Python package with
``pip install openslide-bin``.

On Linux and macOS, the easiest way to get both components is to install_
with a package manager that packages both, such as Anaconda_, DNF or Apt on
Linux systems, or MacPorts_ on macOS systems. You can also install
On Linux and macOS, you can also install_ both OpenSlide and OpenSlide
Python with a package manager that packages both, such as Anaconda_, DNF or
Apt on Linux systems, or MacPorts_ on macOS systems. Or, you can install
OpenSlide Python with pip_ after installing OpenSlide with a package manager
or from source_. Except for pip, do not mix OpenSlide and OpenSlide Python
from different package managers (for example, OpenSlide from MacPorts and
OpenSlide Python from Anaconda), since you'll get library conflicts.

On Windows, download the OpenSlide `Windows binaries`_ and extract them
to a known path. Then, import ``openslide`` inside a
On Windows, you can also download the OpenSlide `Windows binaries`_ and
extract them to a known path. Then, import ``openslide`` inside a
``with os.add_dll_directory()`` statement::

# The path can also be read from a config file, etc.
Expand All @@ -73,6 +76,7 @@ to a known path. Then, import ``openslide`` inside a
else:
import openslide

.. _openslide-bin: https://pypi.org/project/openslide-bin/
.. _install: https://openslide.org/download/#distribution-packages
.. _Anaconda: https://anaconda.org/
.. _MacPorts: https://www.macports.org/
Expand Down
22 changes: 19 additions & 3 deletions openslide/lowlevel.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@


def _load_library():
try:
import openslide_bin

return openslide_bin.libopenslide1
except (AttributeError, ModuleNotFoundError):
pass

def try_load(names):
for name in names:
try:
Expand All @@ -66,7 +73,9 @@ def try_load(names):
except FileNotFoundError:
raise ModuleNotFoundError(
"Couldn't locate OpenSlide DLL. "
"Did you call os.add_dll_directory()? "
"Try `pip install openslide-bin`, "
"or if you're using an OpenSlide binary package, "
"ensure you've called os.add_dll_directory(). "
"https://openslide.org/api/python/#installing"
)
elif platform.system() == 'Darwin':
Expand All @@ -82,12 +91,19 @@ def try_load(names):
if lib is None:
raise ModuleNotFoundError(
"Couldn't locate OpenSlide dylib. "
"Is OpenSlide installed correctly? "
"Try `pip install openslide-bin`. "
"https://openslide.org/api/python/#installing"
)
return cdll.LoadLibrary(lib)
else:
return try_load(['libopenslide.so.1', 'libopenslide.so.0'])
try:
return try_load(['libopenslide.so.1', 'libopenslide.so.0'])
except OSError:
raise ModuleNotFoundError(
"Couldn't locate OpenSlide shared library. "
"Try `pip install openslide-bin`. "
"https://openslide.org/api/python/#installing"
)


_lib = _load_library()
Expand Down

0 comments on commit fa9f1be

Please sign in to comment.