From 9d8ec5ca99ca88413ec6c75fc0a858a339fcb72d Mon Sep 17 00:00:00 2001 From: Benjamin Gilbert Date: Thu, 16 Apr 2026 11:26:49 -0500 Subject: [PATCH] Remove conditionals for Python 3.10 features We no longer support Python 3.9. Signed-off-by: Benjamin Gilbert --- examples/deepzoom/deepzoom_multiserver.py | 27 ++++++-------- examples/deepzoom/deepzoom_server.py | 27 ++++++-------- examples/deepzoom/deepzoom_tile.py | 27 +++++++------- openslide/deepzoom.py | 6 +--- openslide/lowlevel.py | 44 ++++++++++------------- 5 files changed, 53 insertions(+), 78 deletions(-) diff --git a/examples/deepzoom/deepzoom_multiserver.py b/examples/deepzoom/deepzoom_multiserver.py index 2dc00e9..5f74275 100755 --- a/examples/deepzoom/deepzoom_multiserver.py +++ b/examples/deepzoom/deepzoom_multiserver.py @@ -28,16 +28,12 @@ import os from pathlib import Path, PurePath from threading import Lock -from typing import TYPE_CHECKING, Any, Literal +from typing import Any, Literal, TypeAlias import zlib from flask import Flask, Response, abort, make_response, render_template, url_for from PIL import Image, ImageCms -if TYPE_CHECKING: - # Python 3.10+ - from typing import TypeAlias - if os.name == 'nt': _dll_path = os.getenv('OPENSLIDE_PATH') if _dll_path is not None: @@ -68,17 +64,16 @@ ) SRGB_PROFILE = ImageCms.getOpenProfile(BytesIO(SRGB_PROFILE_BYTES)) -if TYPE_CHECKING: - ColorMode: TypeAlias = Literal[ - 'default', - 'absolute-colorimetric', - 'perceptual', - 'relative-colorimetric', - 'saturation', - 'embed', - 'ignore', - ] - Transform: TypeAlias = Callable[[Image.Image], None] +ColorMode: TypeAlias = Literal[ + 'default', + 'absolute-colorimetric', + 'perceptual', + 'relative-colorimetric', + 'saturation', + 'embed', + 'ignore', +] +Transform: TypeAlias = Callable[[Image.Image], None] class DeepZoomMultiServer(Flask): diff --git a/examples/deepzoom/deepzoom_server.py b/examples/deepzoom/deepzoom_server.py index 43bb7c5..84e95b7 100755 --- a/examples/deepzoom/deepzoom_server.py +++ b/examples/deepzoom/deepzoom_server.py @@ -27,17 +27,13 @@ import os from pathlib import Path import re -from typing import TYPE_CHECKING, Any, Literal +from typing import Any, Literal, TypeAlias from unicodedata import normalize import zlib from flask import Flask, Response, abort, make_response, render_template, url_for from PIL import Image, ImageCms -if TYPE_CHECKING: - # Python 3.10+ - from typing import TypeAlias - if os.name == 'nt': _dll_path = os.getenv('OPENSLIDE_PATH') if _dll_path is not None: @@ -70,17 +66,16 @@ ) SRGB_PROFILE = ImageCms.getOpenProfile(BytesIO(SRGB_PROFILE_BYTES)) -if TYPE_CHECKING: - ColorMode: TypeAlias = Literal[ - 'default', - 'absolute-colorimetric', - 'perceptual', - 'relative-colorimetric', - 'saturation', - 'embed', - 'ignore', - ] - Transform: TypeAlias = Callable[[Image.Image], None] +ColorMode: TypeAlias = Literal[ + 'default', + 'absolute-colorimetric', + 'perceptual', + 'relative-colorimetric', + 'saturation', + 'embed', + 'ignore', +] +Transform: TypeAlias = Callable[[Image.Image], None] class DeepZoomServer(Flask): diff --git a/examples/deepzoom/deepzoom_tile.py b/examples/deepzoom/deepzoom_tile.py index 3da7b81..0037334 100755 --- a/examples/deepzoom/deepzoom_tile.py +++ b/examples/deepzoom/deepzoom_tile.py @@ -34,16 +34,12 @@ import re import shutil import sys -from typing import TYPE_CHECKING, Literal +from typing import TYPE_CHECKING, Literal, TypeAlias from unicodedata import normalize import zlib from PIL import Image, ImageCms -if TYPE_CHECKING: - # Python 3.10+ - from typing import TypeAlias - if os.name == 'nt': _dll_path = os.getenv('OPENSLIDE_PATH') if _dll_path is not None: @@ -76,20 +72,21 @@ ) SRGB_PROFILE = ImageCms.getOpenProfile(BytesIO(SRGB_PROFILE_BYTES)) +ColorMode: TypeAlias = Literal[ + 'default', + 'absolute-colorimetric', + 'perceptual', + 'relative-colorimetric', + 'saturation', + 'embed', + 'ignore', +] +Transform: TypeAlias = Callable[[Image.Image], None] if TYPE_CHECKING: - ColorMode: TypeAlias = Literal[ - 'default', - 'absolute-colorimetric', - 'perceptual', - 'relative-colorimetric', - 'saturation', - 'embed', - 'ignore', - ] + # Python 3.12+ TileQueue: TypeAlias = multiprocessing.queues.JoinableQueue[ tuple[str | None, int, tuple[int, int], Path] | None ] - Transform: TypeAlias = Callable[[Image.Image], None] class TileWorker(Process): diff --git a/openslide/deepzoom.py b/openslide/deepzoom.py index 2761327..9eb89de 100644 --- a/openslide/deepzoom.py +++ b/openslide/deepzoom.py @@ -26,17 +26,13 @@ from io import BytesIO import math -from typing import TYPE_CHECKING +from typing import TypeGuard from xml.etree.ElementTree import Element, ElementTree, SubElement from PIL import Image import openslide -if TYPE_CHECKING: - # Python 3.10+ - from typing import TypeGuard - class DeepZoomGenerator: """Generates Deep Zoom tiles and metadata.""" diff --git a/openslide/lowlevel.py b/openslide/lowlevel.py index 64d9697..adf5eae 100644 --- a/openslide/lowlevel.py +++ b/openslide/lowlevel.py @@ -50,16 +50,13 @@ from itertools import count import os import platform -from typing import TYPE_CHECKING, Any, Protocol, TypeVar, cast +from typing import TYPE_CHECKING, Any, ParamSpec, Protocol, TypeAlias, TypeVar, cast from PIL import Image from . import _convert if TYPE_CHECKING: - # Python 3.10+ - from typing import ParamSpec, TypeAlias - from _convert import _Buffer @@ -199,9 +196,7 @@ def from_param(cls, obj: _OpenSlideCache) -> _OpenSlideCache: return obj -if TYPE_CHECKING: - # Python 3.10+ - Filename: TypeAlias = str | bytes | os.PathLike[Any] +Filename: TypeAlias = str | bytes | os.PathLike[Any] class _filename_p: @@ -314,24 +309,25 @@ def __call__(self, *_args: Any) -> Any: raise OpenSlideVersionError(self._minimum_version) -# gate runtime code that requires ParamSpec, Python 3.10+ -if TYPE_CHECKING: - _P = ParamSpec('_P') - _T = TypeVar('_T', covariant=True) +_P = ParamSpec('_P') +_T = TypeVar('_T', covariant=True) - class _Func(Protocol[_P, _T]): - available: bool - def __call__(self, *args: _P.args) -> _T: ... # type: ignore[valid-type] +class _Func(Protocol[_P, _T]): + available: bool - class _CTypesFunc(_Func[_P, _T]): - restype: type | None - argtypes: list[type] - errcheck: _ErrCheck + def __call__(self, *args: _P.args) -> _T: ... # type: ignore[valid-type] - _ErrCheck: TypeAlias = ( - Callable[[Any, _CTypesFunc[..., Any], tuple[Any, ...]], Any] | None - ) + +class _CTypesFunc(_Func[_P, _T]): + restype: type | None + argtypes: list[type] + errcheck: _ErrCheck + + +_ErrCheck: TypeAlias = ( + Callable[[Any, _CTypesFunc[..., Any], tuple[Any, ...]], Any] | None +) # resolve and return an OpenSlide function with the specified properties @@ -361,11 +357,7 @@ def _wraps_funcs( wrapped: list[_Func[..., Any]], ) -> Callable[[Callable[_P, _T]], _Func[_P, _T]]: def decorator(fn: Callable[_P, _T]) -> _Func[_P, _T]: - if TYPE_CHECKING: - # requires ParamSpec, Python 3.10+ - f = cast(_Func[_P, _T], fn) - else: - f = fn + f = cast('_Func[_P, _T]', fn) f.available = True for w in wrapped: f.available = f.available and w.available