Skip to content

Commit

Permalink
paquo.images: use image_provider abstraction in the correct way
Browse files Browse the repository at this point in the history
the previous implementation of add_image was not correctly using the
image_provider abstraction.

This commit fixes add_image and the default SimpleImageProvider
  • Loading branch information
ap-- committed Aug 12, 2020
1 parent 0ea3b13 commit 6bda0df
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 18 deletions.
38 changes: 29 additions & 9 deletions paquo/images.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import json
import pathlib
import re
from abc import ABC, abstractmethod
from collections.abc import MutableMapping, Hashable
from copy import deepcopy
from enum import Enum
from pathlib import Path, PureWindowsPath, PurePath, PurePosixPath
from typing import Iterator, Optional, Any, List, Dict
from typing import Iterator, Optional, Any, List, Dict, Union

from paquo._base import QuPathBase
from paquo._logging import redirect, get_logger
Expand Down Expand Up @@ -94,25 +95,44 @@ def __subclasshook__(cls, C):
return NotImplemented


SimpleFileImageId = Union[str, pathlib.Path]


# noinspection PyMethodMayBeStatic
class SimpleURIImageProvider:
"""simple image provider that uses the files uri as it's identifier"""

class FilenamePathId(str):
"""an id that uses the filename as it's identifier"""
def __eq__(self, other):
return Path(self).name == Path(other).name

def __hash__(self):
return hash(Path(self).name)

def __repr__(self): # pragma: no cover
p = Path(self)
return f'<FilenamePathId("{p.name}") @ "{p.parent}">'

class URIString(str):
"""string uri's can differ in their string representation and still be identical"""
# we need some way to normalize uris
def __eq__(self, other):
return ImageProvider.compare_uris(self, other)
__hash__ = str.__hash__ # fixme: this is not correct!

def uri(self, image_id: str) -> str:
# fixme: this is currently not being called and i think it
# should in the default implementation... need to figure
# out where this needs to be used in QuPathProject
return image_id

def id(self, uri: str) -> str:
return self.URIString(uri)
def uri(self, image_id: SimpleFileImageId) -> 'URIString':
"""accepts a path and returns a URIString"""
if not isinstance(image_id, (Path, str, SimpleURIImageProvider.FilenamePathId)):
raise TypeError("image_id not of correct format") # pragma: no cover
img_path = pathlib.Path(image_id).absolute().resolve()
return SimpleURIImageProvider.URIString(img_path.as_uri())

def id(self, uri: URIString) -> str:
"""accepts a uri string and returns a FilenamePathId"""
if not isinstance(uri, (str, SimpleURIImageProvider.URIString)):
raise TypeError("uri not of correct format") # pragma: no cover
return SimpleURIImageProvider.FilenamePathId(ImageProvider.path_from_uri(uri))

def rebase(self, *uris: str, **kwargs) -> List[Optional[str]]:
uri2uri = kwargs.pop('uri2uri', {})
Expand Down
20 changes: 11 additions & 9 deletions paquo/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,28 +224,30 @@ def _stage_image_entry(self, server_builder):

@redirect(stderr=True, stdout=True) # type: ignore
def add_image(self,
filename: Union[str, pathlib.Path],
image_id: Any, # this should actually be ID type of the image provider
image_type: Optional[QuPathImageType] = None,
*,
allow_duplicates: bool = False) -> QuPathProjectImageEntry:
"""add an image to the project
Parameters
----------
filename:
filename pointing to the image file
image_id:
image_id pointing to the image file (with default image_provider: filename)
image_type:
provide an image type for the image. If not provided the user will
be prompted before opening the image in QuPath.
allow_duplicates:
check if file has already been added to the project.
"""
img_path = pathlib.Path(filename).absolute()

# test if we may add:
img_uri = ImageProvider.uri_from_path(img_path)
img_uri = self._image_provider.uri(image_id)
img_id = self._image_provider.id(img_uri)
if not img_id == image_id:
_log.warning(f"image_provider roundtrip error: '{image_id}' -> uri -> '{img_id}'")
raise RuntimeError("the image provider failed to roundtrip the image id correctly")

if not allow_duplicates:
for entry in self.images:
uri = self._image_provider.id(entry.uri)
Expand All @@ -256,10 +258,10 @@ def add_image(self,
try:
support = ImageServerProvider.getPreferredUriImageSupport(
BufferedImage,
String(str(img_path))
String(str(img_uri))
)
except IOException:
raise FileNotFoundError(filename)
raise FileNotFoundError(image_id)
if not support:
raise IOError("no preferred support found") # pragma: no cover
server_builders = list(support.getBuilders())
Expand All @@ -273,7 +275,7 @@ def add_image(self,
server = server_builder.build()
except IOException:
_, _, _sb = server_builder.__class__.__name__.rpartition(".")
raise IOError(f"{_sb} can't open {img_path}")
raise IOError(f"{_sb} can't open {str(image_id)}")
j_entry.setImageName(ServerTools.getDisplayableImageName(server))

# add some informative logging
Expand Down

0 comments on commit 6bda0df

Please sign in to comment.