-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add validation capability #11
Comments
There's a mechanism for performing this validation for options parsed from environment variables, but I can see how this would be useful also for options provided by From a developer/user perspective, at what point in the process do you see this validation being asserted? Would it be only on access of the option using any of the That seems pretty manageable to me. I'd hesitate a bit with anything that tries to be extra clever and inject some validation code into |
Because one can use delayed evaluation with the options package, I was also thinking the assertion (or any post-processing step) would be performed in
Or one could create and export a generic for
Then, any package author can extend the For example:
|
Before exploring ideas too much further, can you help me understand your use case? I'm sure this isn't a revelation to you, but the simplest solution would be to just wrap the option getter in your own function. I'm sure you have good reasons for feeling like this is less than desirable, and I'm just curious to hear what they are. get_my_opt <- function(name, ...) {
value <- opt(name)
# do post-processing here
} Assuming we do want to add some new features to handle such cases, I do like both your suggestions.
Maybe combining these ideas, a generic could be called on the result of retrieving the option (like in the first example). By default, it would return the raw output value, but you could easily intercept the post-processing with your own method (like in the second example). I'm imagining something like: opt <- function(x, default, env = parent.frame(), ...) {
value <- # [.. omitted for brevity ..]
option_name_type <- paste0("option_named_", x)
type <- structure(list(), class = c(option_named_type, "option_value")
option_process(value, type)
}
option_process <- function(value, type, ...) {
UseMethod("option_process", type)
}
option_process.default <- function(value, ...) {
value
} Sub-types could be provided as an option to I like this because it's very flexible, but it does add an extra method dispatch into retrieving the option and the subtyping would introduce new metadata into the Since this does add a new interface to the package, I'd probably like to take my time exploring a few implementations before settling on a direction. I'm certainly open to the idea, but since it's such a broadly useful feature, I think it will take a bit of trial and error to get right. |
My use case is that I want to be able to define configurable parameters whose values can be provided as environment variables if my package is called from an external tool (e.g., an orchestrator), or as options if called within R, and I want to document the interface. I also want to ensure that the value that I get from Note that the simple wrapper you mentioned does not have access to the full specification of the option, only to its value. This means I have to store the validator function accompanying the given option somewhere else, which is doable but cumbersome. So I can do two things:
One can then specify the configuration parameters in a yaml file (could be json, too): params:
- name: ds_env
desc: |-
Environment (PROD, STAGE, DEV, etc.) to assume in forecast pipelines.
Must be a string.
validator: !expr checkmate::assert_string
default: "DEV"
- name: uuid
desc: |-
Unique string to identify a forecast run, used e.g. for filenames.
default: !expr forecast_uuid() |> quote()
quoted: true
validator: !expr checkmate::assert_string
- name: forecast_start_date
desc: |-
The date or timestamp when the forecast is generated. Can be in the past,
e.g., for back-testing.
Must be a single timestamp or date-like value.
validator: !expr |-
function(x) {
checkmate::assert_multi_class(
x, c("POSIXt", "Date"), .var.name = "forecast_start_date"
)
checkmate::assert_vector(x, len = 1L, .var.name = "forecast_start_date")
}
default: !expr Sys.time() |> quote()
quoted: true
envvar_fn: !expr |-
function(x) {
as.POSIXct(
x, tz = "UTC",
tryFormats = c(
"%Y-%m-%dT%H:%M:%OSZ",
"%Y/%m/%dT%H:%M:%OSZ",
"%Y-%m-%d %H:%M:%OS",
"%Y/%m/%d %H:%M:%OS",
"%Y-%m-%d %H:%M",
"%Y/%m/%d %H:%M",
"%Y-%m-%d",
"%Y/%m/%d"
)
)
} |
Ah, I see. So in this case, the options themselves aren't already known - they're user-configurable. I think the postprocessing function is probably the best bet here. The recursion thing bugs me a bit, but I think if someone is digging this far into the package, they're probably savvy enough to work around it. I'd probably go with something like flowchart LR
A["`envvar`"] -->|"`envvar_fn()`"| B["`option value`"] -->|"`option_fn()`"| C["`return value`"]
|
Yeah, I agree the postprocessing function is the most general solution, and I find As for the infinite recursion if option assertions have circular dependency, I think this is an edge case which is hard to resolve at the level of individual options. For that use case, one shall implement an If |
In production, it must be often ensured that the (evaluated) value of a given option obeys specific rules. E.g., it must be a string following a particular regex pattern, or a numeric value in a given range, etc. Could we add a
validator
or apostprocessor
field tooption_spec()
? I am happy to create a PR if you agree.The text was updated successfully, but these errors were encountered: