Skip to content

Commit 9e853e6

Browse files
committed
Support openslide-bin
Prefer loading OpenSlide from the openslide-bin package if available. For ease of use, document openslide-bin as the preferred installation option. Don't document manual installation of openslide-bin binaries on Linux and macOS, since there isn't generally an advantage to doing that, but leave the existing Windows Zip instructions for compatibility and to explain os.add_dll_directory(). Add an explanatory ModuleNotFoundError on Linux if OpenSlide is not found. Signed-off-by: Benjamin Gilbert <[email protected]>
1 parent ab1dbfe commit 9e853e6

File tree

4 files changed

+56
-17
lines changed

4 files changed

+56
-17
lines changed

.github/workflows/python.yml

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,11 @@ jobs:
4444
matrix:
4545
os: [ubuntu-latest, macos-latest]
4646
python-version: [3.8, 3.9, "3.10", "3.11", "3.12", "3.13-dev"]
47+
openslide: [system, wheel]
4748
include:
4849
- os: ubuntu-latest
4950
python-version: "3.12"
51+
openslide: system
5052
sdist: sdist
5153
# Python 3.8 is too old to support universal binaries, and
5254
# setup-python's Python 3.9 and 3.10 won't build them. Use the
@@ -86,7 +88,8 @@ jobs:
8688
run: |
8789
python -m pip install --upgrade pip
8890
pip install auditwheel build jinja2 pytest
89-
- name: Install OpenSlide
91+
- name: Install OpenSlide (system)
92+
if: matrix.openslide == 'system'
9093
run: |
9194
case "${{ matrix.os }}" in
9295
ubuntu-latest)
@@ -99,6 +102,9 @@ jobs:
99102
brew install openslide
100103
;;
101104
esac
105+
- name: Install OpenSlide (wheel)
106+
if: matrix.openslide == 'wheel'
107+
run: pip install openslide-bin
102108
- name: Build dist
103109
run: |
104110
if [ -z "${{ matrix.sdist }}" ]; then
@@ -124,9 +130,10 @@ jobs:
124130
fi
125131
mkdir -p "artifacts/whl/${{ needs.pre-commit.outputs.dist-base }}"
126132
mv dist/* "artifacts/whl/${{ needs.pre-commit.outputs.dist-base }}"
127-
# save version-specific wheels and oldest abi3 wheel
133+
# from system builds, save version-specific wheels and oldest abi3 wheel
128134
python -c 'import sys
129-
if sys.version_info < (3, 12): print("archive_wheel=1")' >> $GITHUB_ENV
135+
if sys.version_info < (3, 12) and "${{ matrix.openslide }}" == "system":
136+
print("archive_wheel=1")' >> $GITHUB_ENV
130137
- name: Install
131138
run: pip install artifacts/whl/${{ needs.pre-commit.outputs.dist-base }}/*.whl
132139
- name: Run tests
@@ -158,6 +165,7 @@ jobs:
158165
strategy:
159166
matrix:
160167
python-version: [3.8, 3.9, "3.10", "3.11", "3.12", "3.13-dev"]
168+
openslide: [zip, wheel]
161169
steps:
162170
- name: Check out repo
163171
uses: actions/checkout@v4
@@ -169,7 +177,8 @@ jobs:
169177
run: |
170178
python -m pip install --upgrade pip
171179
pip install build flask pytest
172-
- name: Install OpenSlide
180+
- name: Install OpenSlide (zip)
181+
if: matrix.openslide == 'zip'
173182
env:
174183
GH_TOKEN: ${{ github.token }}
175184
run: |
@@ -184,14 +193,18 @@ jobs:
184193
--pattern "${zipname}.zip"
185194
7z x ${zipname}.zip
186195
echo "OPENSLIDE_PATH=c:\\openslide\\${zipname}\\bin" >> $GITHUB_ENV
196+
- name: Install OpenSlide (wheel)
197+
if: matrix.openslide == 'wheel'
198+
run: pip install openslide-bin
187199
- name: Build wheel
188200
run: |
189201
python -m build -w
190202
mkdir -p "artifacts/whl/${{ needs.pre-commit.outputs.dist-base }}"
191203
mv dist/*.whl "artifacts/whl/${{ needs.pre-commit.outputs.dist-base }}"
192-
# save version-specific wheels and oldest abi3 wheel
204+
# from zip builds, save version-specific wheels and oldest abi3 wheel
193205
python -c 'import sys
194-
if sys.version_info < (3, 12): print("archive_wheel=1")' >> $GITHUB_ENV
206+
if sys.version_info < (3, 12) and "${{ matrix.openslide }}" == "zip":
207+
print("archive_wheel=1")' >> $GITHUB_ENV
195208
- name: Install
196209
run: pip install artifacts/whl/${{ needs.pre-commit.outputs.dist-base }}/*.whl
197210
- name: Run tests

README.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,15 @@ OpenSlide can read virtual slides in several formats:
4848

4949
## Installation
5050

51-
OpenSlide Python requires [OpenSlide]. For instructions on installing both
52-
components so OpenSlide Python can find OpenSlide, see the package
53-
[documentation][installing].
51+
OpenSlide Python requires [OpenSlide]. Install both components from PyPI
52+
with:
53+
54+
```console
55+
pip install openslide-python openslide-bin
56+
```
57+
58+
Or, see the [OpenSlide Python documentation][installing] for instructions on
59+
installing so OpenSlide Python can find OpenSlide.
5460

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

doc/index.rst

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,20 @@ Installing
4949
==========
5050

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

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

61-
On Windows, download the OpenSlide `Windows binaries`_ and extract them
62-
to a known path. Then, import ``openslide`` inside a
64+
On Windows, you can also download the OpenSlide `Windows binaries`_ and
65+
extract them to a known path. Then, import ``openslide`` inside a
6366
``with os.add_dll_directory()`` statement::
6467

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

79+
.. _openslide-bin: https://pypi.org/project/openslide-bin/
7680
.. _install: https://openslide.org/download/#distribution-packages
7781
.. _Anaconda: https://anaconda.org/
7882
.. _MacPorts: https://www.macports.org/

openslide/lowlevel.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@
5252

5353

5454
def _load_library():
55+
try:
56+
import openslide_bin
57+
58+
return openslide_bin.libopenslide1
59+
except (AttributeError, ModuleNotFoundError):
60+
pass
61+
5562
def try_load(names):
5663
for name in names:
5764
try:
@@ -66,7 +73,9 @@ def try_load(names):
6673
except FileNotFoundError:
6774
raise ModuleNotFoundError(
6875
"Couldn't locate OpenSlide DLL. "
69-
"Did you call os.add_dll_directory()? "
76+
"Try `pip install openslide-bin`, "
77+
"or if you're using an OpenSlide binary package, "
78+
"ensure you've called os.add_dll_directory(). "
7079
"https://openslide.org/api/python/#installing"
7180
)
7281
elif platform.system() == 'Darwin':
@@ -82,12 +91,19 @@ def try_load(names):
8291
if lib is None:
8392
raise ModuleNotFoundError(
8493
"Couldn't locate OpenSlide dylib. "
85-
"Is OpenSlide installed correctly? "
94+
"Try `pip install openslide-bin`. "
8695
"https://openslide.org/api/python/#installing"
8796
)
8897
return cdll.LoadLibrary(lib)
8998
else:
90-
return try_load(['libopenslide.so.1', 'libopenslide.so.0'])
99+
try:
100+
return try_load(['libopenslide.so.1', 'libopenslide.so.0'])
101+
except OSError:
102+
raise ModuleNotFoundError(
103+
"Couldn't locate OpenSlide shared library. "
104+
"Try `pip install openslide-bin`. "
105+
"https://openslide.org/api/python/#installing"
106+
)
91107

92108

93109
_lib = _load_library()

0 commit comments

Comments
 (0)