Skip to content

Commit

Permalink
Indirect File click types (#1124)
Browse files Browse the repository at this point in the history
Add the ability to reference an imagestack or a codebook through a set of conversion recipes.  Each conversion recipe can read the input given on the CLI and make an attempt to convert it to the desired type.

For instance, a primary image can be referred to by the json describing the slicedimage set, _or_ @experiment.json[my_fov_name][primary].
  • Loading branch information
ttung authored Apr 5, 2019
1 parent 16bb36e commit 911f024
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 0 deletions.
63 changes: 63 additions & 0 deletions starfish/util/click/indirectparams.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import abc
from typing import Generic, Iterable, TypeVar

from starfish.codebook.codebook import Codebook
from starfish.imagestack.imagestack import ImageStack
from starfish.util.indirectfile import (
ConversionRecipe,
convert,
GetCodebook,
GetCodebookFromExperiment,
GetImageStack,
GetImageStackFromExperiment,
NoApplicableConversionRecipeError,
NoSuccessfulConversionRecipeError,
)
from . import ParamType


IndirectResultType = TypeVar("IndirectResultType")


class IndirectFile(ParamType, Generic[IndirectResultType]):
def convert(self, value: str, param, ctx):
conversion_recipes = self.get_conversion_recipes()
try:
return convert(value, conversion_recipes)
except (NoApplicableConversionRecipeError, NoSuccessfulConversionRecipeError) as ex:
self.fail(ex.args[0])

@abc.abstractmethod
def get_conversion_recipes(self) -> Iterable[ConversionRecipe[IndirectResultType]]:
"""Return one or more conversion recipes to get from an input string to the type of object
we want.
"""
raise NotImplementedError()


class CodebookParam(IndirectFile[Codebook]):
def __init__(self):
self.name = "codebook"

def get_conversion_recipes(self) -> Iterable[ConversionRecipe[Codebook]]:
return [
GetCodebookFromExperiment(),
GetCodebook(),
]


CodebookParamType = CodebookParam()


class ImageStackParam(IndirectFile[ImageStack]):
def __init__(self):
self.name = "imagestack"

def get_conversion_recipes(self) -> Iterable[ConversionRecipe[ImageStack]]:
return [
GetImageStackFromExperiment(),
GetImageStack(),
]


ImageStackParamType = ImageStackParam()
8 changes: 8 additions & 0 deletions starfish/util/indirectfile/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from ._base import (
ConversionRecipe,
convert,
NoApplicableConversionRecipeError,
NoSuccessfulConversionRecipeError,
)
from ._codebook import GetCodebook, GetCodebookFromExperiment
from ._imagestack import GetImageStack, GetImageStackFromExperiment
45 changes: 45 additions & 0 deletions starfish/util/indirectfile/_base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import abc
from typing import Generic, Iterable, TypeVar


RecipeResultType = TypeVar("RecipeResultType")


class ConversionRecipe(Generic[RecipeResultType]):
@abc.abstractmethod
def applicable(self, input_parameter: str) -> bool:
raise NotImplementedError()

@abc.abstractmethod
def load(self, input_parameter: str) -> RecipeResultType:
"""Attempt to run this conversion recipe against this input."""
raise NotImplementedError()


class NoApplicableConversionRecipeError(Exception):
"""Raised when no conversion recipe declared itself applicable to this input string."""
pass


class NoSuccessfulConversionRecipeError(Exception):
"""Raised when all the conversion recipes that declared itself applicable failed to execute
successfully."""
pass


def convert(value: str, conversion_recipes: Iterable[ConversionRecipe]):
none_applied = True

for conversion_recipe in conversion_recipes:
if conversion_recipe.applicable(value):
none_applied = False
try:
return conversion_recipe.load(value)
except Exception:
pass

if none_applied:
raise NoApplicableConversionRecipeError(
f"Could not find applicable gonversion recipe for {value}")
raise NoSuccessfulConversionRecipeError(
f"All applicable conversion recipes failed to run successfully for {value}.")
21 changes: 21 additions & 0 deletions starfish/util/indirectfile/_codebook.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from starfish.codebook.codebook import Codebook
from starfish.experiment.experiment import Experiment
from starfish.util.indirectfile._base import ConversionRecipe


class GetCodebookFromExperiment(ConversionRecipe):
def applicable(self, input_parameter: str) -> bool:
return input_parameter.startswith("@")

def load(self, input_parameter: str) -> Codebook:
path = input_parameter[1:]
experiment = Experiment.from_json(path)
return experiment.codebook


class GetCodebook(ConversionRecipe):
def applicable(self, input_parameter: str) -> bool:
return not input_parameter.startswith("@")

def load(self, input_parameter: str) -> Codebook:
return Codebook.from_json(input_parameter)
28 changes: 28 additions & 0 deletions starfish/util/indirectfile/_imagestack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import re

from starfish.experiment.experiment import Experiment
from starfish.imagestack.imagestack import ImageStack
from starfish.util.indirectfile._base import ConversionRecipe


CRE = re.compile("@(?P<path>.+)\[(?P<fov>[^\[\]]+)\]\[(?P<image_type>[^\[\]]+)\]") # noqa: W605


class GetImageStackFromExperiment(ConversionRecipe[ImageStack]):
def applicable(self, input_parameter: str) -> bool:
return CRE.match(input_parameter) is not None

def load(self, input_parameter: str) -> ImageStack:
mo = CRE.match(input_parameter)
assert mo is not None
experiment = Experiment.from_json(mo.group("path"))
fov = experiment[mo.group("fov")]
return fov.get_image(mo.group("image_type"))


class GetImageStack(ConversionRecipe[ImageStack]):
def applicable(self, input_parameter: str) -> bool:
return not CRE.match(input_parameter)

def load(self, input_parameter: str) -> ImageStack:
return ImageStack.from_path_or_url(input_parameter)

0 comments on commit 911f024

Please sign in to comment.