library("rplatypus")
# A specification with two problems, caught and read as data rather than as text.
spec <- platypus_spec(
data = segmentation_data("train", "valid", colormap = binary_colormap),
models = list(u_net("m", input_shape = c(64, 64), loss = "focaal"))
)
tryCatch(
platypus_fit(spec),
platypus_config_error = function(e) {
print(e$problems) # one row per problem: where, problem, got
invisible(NULL)
},
platypus_error = function(e) {
message("something else went wrong: ", conditionMessage(e))
}
)When something is wrong: the conditions this package raises
Description
Every failure that comes back from the engine is raised as a classed condition, so a script can tell one kind of problem from another instead of matching on the message. Until now that was true and undocumented, which made it true and useless.
What the class vector looks like
Three or four entries, outermost first:
platypus_config_error the specific kind platypus_error every failure from this package, which is the one to catch error an ordinary R error condition
So tryCatch(…, platypus_error = ) catches everything this package raises and nothing else, and a specific class narrows it. platypus_error appears once even when the engine sends no kind at all.
What to catch
platypus_error, unless there is a reason not to. The specific kinds come from the engine, so the list below belongs to the version this package pins, and a later engine may add one. Catching the base class is the thing that keeps working.
Two are worth naming on purpose:
-
platypus_config_error -
A specification that does not describe a runnable experiment. This one carries data:
conditionCall()aside, the condition has aproblemsdata frame with one row per problem -where,problemand, when it is short enough to be useful,got- and asourcenaming the YAML file if it came from one. The engine reports every problem it found rather than the first, so the frame is what makes "three things are wrong" readable. -
platypus_training_stopped - Not a defect. A callback ended the run early, which is what it was asked to do, and anything scoring the result afterwards should treat it as a finished run rather than a failure.
The kinds the pinned engine can send
One class per kind, prefixed. tests/testthat/test-conditions.R holds this list to what the engine actually defines, so a kind added upstream fails here rather than arriving undocumented.
platypus_config_error a specification that cannot be run platypus_engine_error the engine could not do what the specification asked platypus_model_error the architecture could not be built as described platypus_data_error the dataset on disk is not what the specification says platypus_image_error an image could not be read, or is not the shape claimed platypus_mask_error a mask does not match its colormap platypus_channel_error one channel per file, and the files do not agree platypus_volume_error a volume, its orientation or its spacing platypus_dicom_error a DICOM file or series platypus_augmentation_error a transform that cannot do what was asked of it platypus_detection_error detection metrics platypus_detection_data_error annotations, boxes or classes platypus_instance_error a separation target, or decoding one into objects platypus_instance_data_error the per-object mask files a case has, or has not platypus_weights_error weights that do not belong to this model platypus_training_stopped a callback ended the run early platypus_unknown_case a case or model name that is not in this run
platypus_unknown_case is the one with no class behind it in the engine: the R bridge raises it itself, because asking for a case that does not exist is a question the bridge can answer without troubling Python.
Why R has more of these than Python does
The engine exports three exception classes - PlatypusError, ConfigError, WeightsError - and raises the other twelve internally, so a Python caller catches PlatypusError and inspects it. R derives its class from the kind the engine sends with every failure, so all of them arrive as catchable names. That is a wider surface on this side, and deliberately so: R has no exception hierarchy to lean on, and inherits() is cheaper than parsing a message.
See Also
platypus_status(), for when the problem is that the engine never started.