Skip to content

Commit

Permalink
Merge pull request #295 from bgilbert/pathlib
Browse files Browse the repository at this point in the history
Switch examples and `jekyll_fix` from `os.path` to `pathlib`
  • Loading branch information
bgilbert authored Oct 27, 2024
2 parents bbbc0eb + 012fbb1 commit ece4466
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 80 deletions.
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

0 comments on commit ece4466

Please sign in to comment.