Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 18 additions & 20 deletions doc/jekyll_fix.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# openslide-python - Python bindings for the OpenSlide library
#
# Copyright (c) 2014 Carnegie Mellon University
# Copyright (c) 2024 Benjamin Gilbert
#
# This library is free software; you can redistribute it and/or modify it
# under the terms of version 2.1 of the GNU Lesser General Public License
Expand All @@ -26,6 +27,7 @@
from __future__ import annotations

import os
from pathlib import Path

from sphinx.application import Sphinx
from sphinx.util import logging
Expand All @@ -49,37 +51,33 @@ def remove_path_underscores(app: Sphinx, exception: Exception | None) -> None:
logger = logging.getLogger(__name__)
logger.info(bold('fixing pathnames... '), nonl=True)
# Rewrite references in HTML/JS files
for dirpath, _, filenames in os.walk(app.outdir):
outdir = Path(app.outdir)
for dirpath, _, filenames in os.walk(outdir):
for filename in filenames:
_, ext = os.path.splitext(filename)
if ext in REWRITE_EXTENSIONS:
path = os.path.join(dirpath, filename)
with open(path, encoding='utf-8') as fh:
path = Path(dirpath) / filename
if path.suffix in REWRITE_EXTENSIONS:
with path.open(encoding='utf-8') as fh:
contents = fh.read()
for old, new in DIRS.items():
contents = contents.replace(old + '/', new + '/')
for old, new in FILES.items():
contents = contents.replace(old, new)
with open(path, 'w', encoding='utf-8') as fh:
with path.open('w', encoding='utf-8') as fh:
fh.write(contents)
# Move directory contents
for old, new in DIRS.items():
olddir = os.path.join(app.outdir, old)
newdir = os.path.join(app.outdir, new)
if not os.path.exists(newdir):
os.mkdir(newdir)
if os.path.isdir(olddir):
for filename in os.listdir(olddir):
oldfile = os.path.join(olddir, filename)
newfile = os.path.join(newdir, filename)
os.rename(oldfile, newfile)
os.rmdir(olddir)
olddir = outdir / old
newdir = outdir / new
newdir.mkdir(exist_ok=True)
if olddir.is_dir():
for oldfile in olddir.iterdir():
oldfile.rename(newdir / oldfile.name)
olddir.rmdir()
# Move files
for old, new in FILES.items():
oldfile = os.path.join(app.outdir, old)
newfile = os.path.join(app.outdir, new)
if os.path.isfile(oldfile):
os.rename(oldfile, newfile)
oldfile = outdir / old
if oldfile.is_file():
oldfile.rename(outdir / new)
logger.info('done')


Expand Down
55 changes: 30 additions & 25 deletions examples/deepzoom/deepzoom_multiserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# deepzoom_multiserver - Example web application for viewing multiple slides
#
# Copyright (c) 2010-2015 Carnegie Mellon University
# Copyright (c) 2021-2023 Benjamin Gilbert
# Copyright (c) 2021-2024 Benjamin Gilbert
#
# This library is free software; you can redistribute it and/or modify it
# under the terms of version 2.1 of the GNU Lesser General Public License
Expand All @@ -27,6 +27,7 @@
from collections.abc import Callable
from io import BytesIO
import os
from pathlib import Path, PurePath
from threading import Lock
from typing import TYPE_CHECKING, Any, Literal
import zlib
Expand Down Expand Up @@ -82,7 +83,7 @@


class DeepZoomMultiServer(Flask):
basedir: str
basedir: Path
cache: _SlideCache


Expand All @@ -94,7 +95,7 @@ class AnnotatedDeepZoomGenerator(DeepZoomGenerator):

def create_app(
config: dict[str, Any] | None = None,
config_file: str | None = None,
config_file: Path | None = None,
) -> Flask:
# Create and configure app
app = DeepZoomMultiServer(__name__)
Expand All @@ -116,7 +117,7 @@ def create_app(
app.config.from_mapping(config)

# Set up cache
app.basedir = os.path.abspath(app.config['SLIDE_DIR'])
app.basedir = Path(app.config['SLIDE_DIR']).resolve(strict=True)
config_map = {
'DEEPZOOM_TILE_SIZE': 'tile_size',
'DEEPZOOM_OVERLAP': 'overlap',
Expand All @@ -131,16 +132,18 @@ def create_app(
)

# Helper functions
def get_slide(path: str) -> AnnotatedDeepZoomGenerator:
path = os.path.abspath(os.path.join(app.basedir, path))
if not path.startswith(app.basedir + os.path.sep):
# Directory traversal
def get_slide(user_path: PurePath) -> AnnotatedDeepZoomGenerator:
try:
path = (app.basedir / user_path).resolve(strict=True)
except OSError:
# Does not exist
abort(404)
if not os.path.exists(path):
if path.parts[: len(app.basedir.parts)] != app.basedir.parts:
# Directory traversal
abort(404)
try:
slide = app.cache.get(path)
slide.filename = os.path.basename(path)
slide.filename = path.name
return slide
except OpenSlideError:
abort(404)
Expand All @@ -152,7 +155,7 @@ def index() -> str:

@app.route('/<path:path>')
def slide(path: str) -> str:
slide = get_slide(path)
slide = get_slide(PurePath(path))
slide_url = url_for('dzi', path=path)
return render_template(
'slide-fullpage.html',
Expand All @@ -163,15 +166,15 @@ def slide(path: str) -> str:

@app.route('/<path:path>.dzi')
def dzi(path: str) -> Response:
slide = get_slide(path)
slide = get_slide(PurePath(path))
format = app.config['DEEPZOOM_FORMAT']
resp = make_response(slide.get_dzi(format))
resp.mimetype = 'application/xml'
return resp

@app.route('/<path:path>_files/<int:level>/<int:col>_<int:row>.<format>')
def tile(path: str, level: int, col: int, row: int, format: str) -> Response:
slide = get_slide(path)
slide = get_slide(PurePath(path))
format = format.lower()
if format != 'jpeg' and format != 'png':
# Not supported by Deep Zoom
Expand Down Expand Up @@ -208,7 +211,7 @@ def __init__(
self.dz_opts = dz_opts
self.color_mode = color_mode
self._lock = Lock()
self._cache: OrderedDict[str, AnnotatedDeepZoomGenerator] = OrderedDict()
self._cache: OrderedDict[Path, AnnotatedDeepZoomGenerator] = OrderedDict()
# Share a single tile cache among all slide handles, if supported
try:
self._tile_cache: OpenSlideCache | None = OpenSlideCache(
Expand All @@ -217,7 +220,7 @@ def __init__(
except OpenSlideVersionError:
self._tile_cache = None

def get(self, path: str) -> AnnotatedDeepZoomGenerator:
def get(self, path: Path) -> AnnotatedDeepZoomGenerator:
with self._lock:
if path in self._cache:
# Move to end of LRU
Expand Down Expand Up @@ -286,13 +289,14 @@ def xfrm(img: Image.Image) -> None:


class _Directory:
def __init__(self, basedir: str, relpath: str = ''):
self.name = os.path.basename(relpath)
_DEFAULT_RELPATH = PurePath('.')

def __init__(self, basedir: Path, relpath: PurePath = _DEFAULT_RELPATH):
self.name = relpath.name
self.children: list[_Directory | _SlideFile] = []
for name in sorted(os.listdir(os.path.join(basedir, relpath))):
cur_relpath = os.path.join(relpath, name)
cur_path = os.path.join(basedir, cur_relpath)
if os.path.isdir(cur_path):
for cur_path in sorted((basedir / relpath).iterdir()):
cur_relpath = relpath / cur_path.name
if cur_path.is_dir():
cur_dir = _Directory(basedir, cur_relpath)
if cur_dir.children:
self.children.append(cur_dir)
Expand All @@ -301,9 +305,9 @@ def __init__(self, basedir: str, relpath: str = ''):


class _SlideFile:
def __init__(self, relpath: str):
self.name = os.path.basename(relpath)
self.url_path = relpath
def __init__(self, relpath: PurePath):
self.name = relpath.name
self.url_path = relpath.as_posix()


if __name__ == '__main__':
Expand Down Expand Up @@ -336,7 +340,7 @@ def __init__(self, relpath: str):
),
)
parser.add_argument(
'-c', '--config', metavar='FILE', dest='config', help='config file'
'-c', '--config', metavar='FILE', type=Path, dest='config', help='config file'
)
parser.add_argument(
'-d',
Expand Down Expand Up @@ -396,6 +400,7 @@ def __init__(self, relpath: str):
parser.add_argument(
'SLIDE_DIR',
metavar='SLIDE-DIRECTORY',
type=Path,
nargs='?',
help='slide directory',
)
Expand Down
10 changes: 6 additions & 4 deletions examples/deepzoom/deepzoom_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from collections.abc import Callable
from io import BytesIO
import os
from pathlib import Path
import re
from typing import TYPE_CHECKING, Any, Literal, Mapping
from unicodedata import normalize
Expand Down Expand Up @@ -93,7 +94,7 @@ class DeepZoomServer(Flask):

def create_app(
config: dict[str, Any] | None = None,
config_file: str | None = None,
config_file: Path | None = None,
) -> Flask:
# Create and configure app
app = DeepZoomServer(__name__)
Expand All @@ -113,9 +114,9 @@ def create_app(
app.config.from_mapping(config)

# Open slide
slidefile = app.config['DEEPZOOM_SLIDE']
if slidefile is None:
if app.config['DEEPZOOM_SLIDE'] is None:
raise ValueError('No slide file specified')
slidefile = Path(app.config['DEEPZOOM_SLIDE'])
config_map = {
'DEEPZOOM_TILE_SIZE': 'tile_size',
'DEEPZOOM_OVERLAP': 'overlap',
Expand Down Expand Up @@ -273,7 +274,7 @@ def xfrm(img: Image.Image) -> None:
),
)
parser.add_argument(
'-c', '--config', metavar='FILE', dest='config', help='config file'
'-c', '--config', metavar='FILE', type=Path, dest='config', help='config file'
)
parser.add_argument(
'-d',
Expand Down Expand Up @@ -333,6 +334,7 @@ def xfrm(img: Image.Image) -> None:
parser.add_argument(
'DEEPZOOM_SLIDE',
metavar='SLIDE',
type=Path,
nargs='?',
help='slide file',
)
Expand Down
Loading
Loading